今天又一题,单调队列leetcode862

关于子数组和的问题,容易想到前缀和,但是一般前缀和还不足够优化时间复杂度。看到一个大于五个零的数组长度,我就知道这题应该只能O(n)了。然后首先想到双指针,指了半天发现中间有负数也就是左端点可能不行,但是左端点往右可能又行了,这种情况又不会处理了。这时候我想到了一种叫单调栈的东西,感觉这东西应该是能解决这问题。但是不太熟练,没想出来。

于是看了答案,答案说用单调双端队列。

首先第一点是一个点入队列时是充当子数组的右端点,出队列时充当子数组的左端点。(有机会充当,并不一定会)。这一点就是常规的双指针遍历的思想。

第二点是已在队列中的点出队列的时候是在它成为满足题目条件的一个子数组的左端点之后,让它出队列。这一点很好理解,这个点如果呆着不出去,后面来的右端点只可能在当前右端点右边,肯定比当前这个长度大,所以它已经无用了,于是将它弄出去。

第三点是遍历前缀和数组时每个点是怎么进队列的。首先某个点在进队列时它即将进入队列尾并充当可能结果的右边界。这时要知道,它前面已经在队列里的人都已经当完右边界并且被榨干价值了。因此队列里的每个人都只剩下可能在前面人出队后自己顶上并充当左边界,这一点价值了。这时候就不得不提到,某些人已经不再适合充当左边界,也就是可以提前将它们踢掉。这些人的下标比新来者小,但是如果他们的前缀和还比新来者大呢?也就是说,如果后面某位新来的右边界来了并且能和他们配对,那么也一定更能和目前的新来者配对,并且目前新来者作为左边界下标还在这些人的右面,也就是结果一定更优,因此这些在队列中以为自己还剩下左边界价值的可怜人们就得被提前踢掉了。踢完之后当前新来者即可入队,值得注意的是在入队时刻他即完成右边界任务,之后呆在队列里开始等待作为左边界。

不得不说,单调栈类算法是我在leetcode学习中看到的相当巧妙的算法了。其实出发点就是朴素的遍历,朴素的双指针。只不过科学家们都是勤奋的懒汉,为了实现最朴素的懒惰,完成了最精妙的优化。虽然我的能力远不能做到这一点,但是我可以先完成小目标,想到最朴素的那个想法。至于怎么优化来实现,那得慢慢来了。就比如这一题,我是想到了朴素的双指针,单调队列可能也有点呼之欲出了,虽然还差很远,但是有那个最朴素的想法我觉得就是实现了第一步。

继续努力。

原文地址:https://www.cnblogs.com/agnes6/p/13258335.html