算法-堆+单调栈
堆
首先堆在我们的Java中我们的是一个优先队列类
PriorityQueue
然后我们要弄最大堆和最小堆
最大堆:
PriorityQueue<Integer> pq = new PriorityQueue<Integer>((a, b) -> b - a);
最小堆:
PriorityQueue<Integer> pq = new PriorityQueue<Integer>((a, b) -> a-b);
1046最后一块石头的重量
我们让最大的和第二大的石头相撞,我们就能得到最小的结果
所以我们维护一个最大堆
PriorityQueue<Integer> pq = new PriorityQueue<Integer>((a, b) -> b - a);
2558从数量最多的堆取走礼物
3264K次乘数运算后的最终数组
我们还有一种思路是
我们往堆里面存的类型是数组
ProityQueue<int[]>
然后【0】是我们的值
【1】是我们的这个元素的下标
两个元素按值的大小排序,当值相同是按照谁的下标靠前来排序
PriorityQueue<int[]> pq = new PriorityQueue<int[]>((a, b) -> a[0] != b[0] ? a[0]-b[0] : a[1]-b[1]);
2530执行k次操作后的最大分数
单调栈
739每日温度
我们遇到第一个不是递增的温度的时候,我们前面的节点计算就结束了
所以我们用个栈,来存我们的单调递增的天的下标
然后用变量i,来往后遍历
当遇到我们的第一个不是递增的温度时
我们用 当前变量i减去栈中天的下标
然后把距离差记录到我们的数组里
1475商品折扣后的最终价格
我们的栈是从右往左收集的
看看我们的收集情况和弹出情况
收集:
我们从右往左进行收集我们的元素的值
弹出情况:
如果我们遍历到i这个位置,我们的price【i】比我们栈元素大
说明我们不能打折,我们要右边小于左边才能打折
所以我们不断弹出,直到遇见第一个比我们小的数字,如果stack为空那就是不打折,不为空那就是打折
ps:如果右边弹出完,然后继续遍历左边的值是否有影响?因为我们用不到数组右边那一大段了
不会,因为我们能弹出,就是因为当前元素比栈元素小,
所以当前元素又是最靠左,又比之前栈里的元素小,所以之前右边的元素弹出没有用到就无所谓了
503下一个更大元素
962最大宽度坡
我们首先维护一个单调递减的栈
然后把我们单调递减的值对应的下标放到我们的栈里面
然后我们要求最远距离
所以我们就从右往左进行遍历
为啥一满足条件,就把栈里的元素弹出呢?
因为我们最右边,肯定才是最远的,我们从右往左遍历,最右边那个值n和i的位置的差值
肯定比n-1和i位置的差值大
所以栈里的这个元素的位置,已经没必要使用了
853车队
首先我们要有个Time【】数组,记录各个位置到终点target的时间(起始的时候,每个位置只有一辆车)
然后我们用一个栈来维护我们的结果
因为我们是从地点0往地点target开始遍历的,
所以我们在前面的地点,遇到比我们花费时间久的,也就是我前面地点的Time花费比我后面地点的Time多
就说明他会卡住我们,形成一个车队
所以我们把栈里的pop出,把这个前面的慢车+进去
遇到前面比我们花费时间少的,加进去
说人话,
time【i】>栈里车耗费的时间
我们就把栈里的弹出来,然后把i这个位置的车当作一个组放进去
time【i】<栈里车耗费的时间
说明我们追不上前面,我们把它当作一个组放进去
1124表现良好的最长时间段
前缀知识:前缀和
单调栈解法
劳累天的值为1
不劳累天的值为-1
然后我们要求和>1的最长子数组
看看代码
我们计算前缀和
然后维护一个前缀和单调递减的栈,栈里面存的是我们位置的下标
(说一下为什么我们的栈要单调递减,跳过的比它大的元素怎么办
首先我们的栈是从左往右遍历,而我们的差值的计算是从右往左
例如
3 5 2 1 4
3 5 2 1 6
我们的栈里面是【3,2,1】为啥5没用呢,因为我们从右往左遍历我们遇到的数字有这样的情况
nums【j】>3但是nums【j】<5 这样子的情况,我们就不用考虑5 例如第一个例子 4>3 4<5
nums【j】>5>3 考虑5的情况,也明显是左边的3更远,所以5根本没用 第二个例子 6>5>3
所以单调递减栈外那些比我们栈内元素值大的元素我们都没必要去考虑,你看我们分析的情况下那值对我们根本没影响
)
然后我们从后往前遍历,来算两个位置之间的差值,然后我们的ans保存差值的最大值
(重要)132模式
我们的位置是 i <j <k
然后我们要求的是
nums【j】>nums【k】>nums【i】
其实我们也想到了,这个就是枚举而已,但是我们要枚举哪个好一点呢?
如何在确定一个数后快速找到另外两个数
枚举 i:
由于 i 是 132 结构中最小的数,那么相当于我们要从 i 后面,找到一个对数 (j,k),使得 (j,k) 都满足比 i 大,同时 j 和 k 之间存在 j > k 的关系。由于我们的遍历是单向的,因此我们可以将问题转化为找 k,首先 k 需要比 i 大,同时在 [i, k] 之间存在比 k 大的数即可。
枚举 j:
由于 j 是 132 结构里最大的数,因此我们需要在 j 的右边中比 j 小的「最大」的数,在 j 的左边找比 j 小的「最小」的数。这很容易联想到单调栈,但是朴素的单调栈是帮助我们找到左边或者右边「最近」的数,无法直接满足我们「最大」和「最小」的要求,需要引入额外逻辑。
枚举 k:
由于 k 是 132 结构中的中间值,这里的分析逻辑和「枚举 i」类似,因为遍历是单向的,我们需要找到 k 左边的 i,同时确保 [i,k] 之间存在比 i 和 k 大的数字
开始我们的答案解析
处理过程:
我们从后往前做,维护一个「单调递减」的栈,同时使用 k
记录所有出栈元素的最大值(k
代表满足 132 结构中的 2)
我们从后往前进行遍历
我们出栈的条件是nums【i】>deque.peeLast()
如果我们的K有值的话,就说明我们满足了(J>K)
看看我们的K的逻辑
我们是单调栈不满足单调递减的时候弹出,此时我们的K才有值,不满足单调递减说明我们已经找到了(J>K)所以我们后面就剩找K>i了
这就是为啥我们的nums【i】<k的时候我们return true