滑动窗口最值问题及单调栈


/*
单调栈

给定一个数组,求每一个数,左边比他小的位置在哪,右边比他小的位置在哪,离他最近的

[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作为高,左扩 扩不了,右侧扩可以

求解的问题与范围具有什么单调性


 */


/*
窗口內最大值最小值的更新结构以及单调栈,和堆的地位不相上下
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

metabit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值