/*
单调栈
给定一个数组,求每一个数,左边比他小的位置在哪,右边比他小的位置在哪,离他最近的
[3,2,1,7,0,4,5,6]
暴力解 O(N^2)
单调栈 O(N)
0->3 -1 1->2
1->2 -1 2->1
2->1 -1 4->0
-----------------------------------------
先假设数组中无重复值
[3,4,2,5,6,0,1] 假设数组无重复值
| |
| |
| |
| |
-------
栈
离你最近的 ,小的 栈底到栈顶由小到达
离你最近的,大的 栈底到站定从大到小
整体处理逻辑,从左往右遍历
首先0位置的3 能直接进栈,因为栈中没有数
| |
| |
| |
| 0 | 3
-------
栈
然后来到1位置的4,4可以直接进栈,因为4盖到3上没有改变栈的有小到大的顺序
| |
| |
| 1 | 4
| 0 | 3
-------
栈
下面来到2位置的2,他无法直接进栈,因为他一旦落在4上,就会破坏栈中由小到大的事实
于是从栈里弹出下标,
什么时候生成记录,栈弹出数据时
1位置的4要弹出,让你出来的就是右边离你最近的比你小的数,所以是2
谁是1位置的4左边离你最近的比你小的,就是1位置的4下面压着的0位置的3
弹出1位置的4,2位置的2仍然不能进栈,因为0位置的3要比2位置的2大,不是由小到大
所以0位置的3要弹出,2是让3弹出的数,所以离3最近的右侧的数是2
弹出后栈中没有数,所以 离3最近的左侧的数是 -1 (没有)
此时2进栈,以此类推
| |
| |
| |
| 2 | 2
-------
栈
生成记录的时候,就是数弹出的时候生成
3位置的5直接进栈
4位置的6直接进栈
| |
| 4 | 6
| 3 | 5
| 2 | 2
-------
栈
5位置的0,出来时,会生成一批记录
4位置的6 左边 3位置的5 右边5位置的0
3位置的5 左边 2位置的2 右边5位置的0
2位置的2 左边 -1 右边5位置的0
5位置的0进栈
| |
| |
| |
| 5 | 0
-------
栈
| |
| |
| 6 | 1
| 5 | 0
-------
栈
6位置的1进栈
6位置的1弹出, 右边离他最近比他小的 (无)-1 左边 5位置的0
5位置的0 右边 无 左边 无
一个数X,必须落在比他小的数Y上,否则就弹出Y,Y',Y''
每一个数,谁让他弹出,就是右边离他最近的小的值
左边比他小的,就是栈顶的数下边的数
执行过后
单独出来的,认为右边比他小元素的没有
栈底元素,认为左边比他小的元素没有
| |
| x | a
| y | b
| |
-------
栈
z 位置的 c 要让x 位置的a 弹出
a为什么要压b, b一定要小于a
为什么a压着b b一定比a 早进栈
b < a
b a c
y ... x ... z
c 的出现为什么要 让a 弹出,证明 c < a
这种情况出现的时候,为什么说c是离a最近的,比a小的, b是离a最近的,比a小的
相等的时候咋办?
[ 3, 2, 3, 4, 4, 3, 1]
0 1 2 3 4 5 6
相同放入list
{0} 3
{3} 4
{0,2} 3 做压缩
5位置的3 比 前一个小
全都做结算 压着的最后一个元素 即 2位置的3 3位置的4 5位置的3
2位置的3 4位置的4 5位置的3
{3,4} 4-----清掉了
{0,2} 3 做压缩
*/
type Stack struct {
Elem []interface{}
}
func NewStack() *Stack {
return &Stack{Elem: make([]interface{},0)}
}
func (s *Stack)Push(value interface{}) {
s.Elem = append(s.Elem, value)
}
func (s *Stack)Pop() interface{}{
res := s.Elem[len(s.Elem)-1]
s.Elem = s.Elem[0:len(s.Elem)-1]
return res
}
func (s *Stack)Peek() interface{}{
return s.Elem[len(s.Elem)-1]
}
func (s *Stack)IsEmpty() bool {
return len(s.Elem) == 0
}
func TestStack(t *testing.T) {
stack := NewStack()
stack.Push(100)
stack.Push(200)
stack.Push(300)
stack.Pop()
for !stack.IsEmpty() {
t.Log(stack.Pop())
}
}
type List struct {
Elem []interface{}
}
func NewList() *List {
return &List{Elem: make([]interface{},0)}
}
func (l *List)GetElem() []interface{} {
return l.Elem
}
func (l *List)Add(value interface{}) {
l.Elem = append(l.Elem,value)
}
func (l *List)Get(i int) interface{} {
return l.Elem[i]
}
func (l *List)Size() int {
return len(l.Elem)
}
func getNearLessNoRepeat(arr []int) [][]int {
res := make([][]int,len(arr))
for k := range res {
res[k] = make([]int, 2)
}
stack := NewStack()
for i := 0; i < len(arr); i++ {
for !stack.IsEmpty() && arr[stack.Peek().(int)] > arr[i] {
popIndex := stack.Pop().(int)
leftLessIndex := -1
if !stack.IsEmpty() {
leftLessIndex = stack.Peek().(int)
}
res[popIndex][0] = leftLessIndex
res[popIndex][1] = i
}
stack.Push(i)
}
for !stack.IsEmpty() {
popIndex := stack.Pop().(int)
leftLessIndex := -1
if !stack.IsEmpty() {
leftLessIndex = stack.Peek().(int)
}
res[popIndex][0] = leftLessIndex
res[popIndex][1] = -1
}
return res
}
func TestGetNearLessNoRepeat(t *testing.T) {
fmt.Println(getNearLessNoRepeat([]int{3,4,2,5,6,0,1}))
}
func getNearLess(arr []int) [][]int {
res := make([][]int,len(arr)) // 每个位置俩信息
for k := range res {
res[k] = make([]int, 2)
}
stack := NewStack() // 放下标list 其中放的是位置,同样值的东西,位置压在一起
// 代表值,从底到顶是有小到大的
for i := 0; i < len(arr); i++ {
// 底到顶 小到大
for !stack.IsEmpty() && arr[stack.Peek().(*List).Get(0).(int)] > arr[i] { //i -> arr[i] 进栈
popIs := stack.Pop().(*List)
// 取位于下面位置的列表中,最晚加入的那个
leftLessIndex := -1
if !stack.IsEmpty() {
leftLessIndex = stack.Peek().(*List).Get(stack.Peek().(*List).Size()-1).(int)
}
for _, popi := range popIs.GetElem() {
res[popi.(int)][0] = leftLessIndex
res[popi.(int)][1] = i
}
}
// 相等的,比你小的
if !stack.IsEmpty() && arr[stack.Peek().(*List).Get(0).(int)] == arr[i] {
stack.Peek().(*List).Add(i)
}else {
list := NewList()
list.Add(i)
stack.Push(list)
}
}
for !stack.IsEmpty() {
popIs := stack.Pop().(*List)
//取位于下面的位置的裂变中最晚加入的那个
leftLessIndex := -1
if !stack.IsEmpty() {
leftLessIndex = stack.Peek().(*List).Get(stack.Peek().(*List).Size()-1).(int)
}
for _, popi := range popIs.GetElem() {
res[popi.(int)][0] = leftLessIndex
res[popi.(int)][1] = -1
}
}
return res
}
func TestGetNearLess(t *testing.T) {
t.Log(getNearLess([]int{1,2,0,1,0}))
}
/*
单调栈是什么?
一种特别设计的栈结构,为了解决如下问题:
给定一个可能含有重复值的数组arr,i位置的数一定存在如下两个信息
1 arr[i] 的左侧离i最近并且小于(或者大于)arr[i]的数在哪?
2 arr[i] 的右侧离i最近并且小于(或者大于)arr[i]的数在哪?
如果想得到arr中所有位置的两个信息,怎么能让得到信息的过程尽量快
那么到底该如何设计呢?
*/
/*
题目三
给定一个只包含正整数的数组arr,
arr中任何一个子数组sub,一定都可以算出(sub累加和) * (sub中的最小值)是什么,
那么所有子数组中,这个值最大是多少?
*/
/*
滑动窗口、单调栈怎么用?
想用滑动窗口以,要想办法把具体的问题转化为欢动窗口的处理过程
想用滑动窗口最值的更新结构,就看看处理流程下,是否需要最值这个信息
想用单调栈,要想办法把具体的问题转化为单调栈所解决的原问题
滑动窗口及其最大值和最小值的更新结构,单调栈,都是重要算法原型
*/
/*
预处理技巧
给定一个数组,查询L到R的子数组的累加和,可以用前缀和数组来实现
sum[i] arr[0...i]的累加和
L...R 的累加和
0 ~ R 上的累加和 sum[R]
减
0 ~ L -1 的累加和 sum[L-1]
正整数范围越大,累加和越大
范围和累加和大小 具有单调性
*/
/*
单调栈问题
给一个直方图
5 6 2 1 4
[]
[][]
[][] []
[][] []
[][][] []
[][][][][]
由直方图拼成的最大的长方形面积多大
假设 必须以5作为高,左扩 扩不了,右侧扩可以
求解的问题与范围具有什么单调性
*/
/*
窗口內最大值最小值的更新结构以及单调栈,和堆的地位不相上下
*/
滑动窗口最值问题及单调栈
最新推荐文章于 2022-08-25 18:50:37 发布