您的位置:首页 > 其它

[leetcode刷题系列]Gas Station

2013-09-29 23:38 375 查看
嗯,这是leetcode最新加的一道题目, 也是很经典的算法类的题目了。解法也不止一种。

这里写一下利用双端队列怎么做这道题求出所有的合法的起点把。

1, 单调的双端队列

1)什么是双端队列

双端队列是一个可以在头部和尾部都可以删除和插入元素的队列。站看之下似乎没有什么特别的玄机。但是当我们将其用于扫描一个大小为n的数组,

数组的没个元素都最多进一次,最多被删除一次。那么我们对这个队列的操作复杂度就是N,对双端队列的应用重点就在于何时插入元素,何时删除元素,

以维护某种性质。 接下来我们就用一个例子来介绍单调的双端队列。

2)单调的双端队列

来看一道题目, 对于一个大小为n的数组A,和一个数字m( 1<=m <= n),求出所有的B[i] = min(A[i], A[i +1],..,A[i + m - 1】) (1 <= i <= n - m + 1)。

乍看之下这是一道典型的RMQ的问题,经典的算法都可以在nlogn的时间复杂度下解决这个问题。但是显然这又是一道有自己的特点的RMQ的问题。利用

经典算法必然少挖掘了这个问题的某些特性,尽管经典算法已经做的很好了。下面就介绍如何利用双端队列解决这个问题。

我们建立一个空的双端队列用来保存我们插入其中的数组A的下标,然后开始对数组A进行扫描,假设当前扫描到下表为i的位置,那么如果如果此时

双端队列尾部所保存的下标对应的A数组元素A[deque[tail - 1]] >= A[i]的话, 我们就可以将双端队列尾部元素删除,然后一直循环检测此时双端队列是否

为空或者直到A[deque[tail - 1]] < A[i],那么我们就可以将下表i添加到双端队列的尾部。这样在整个扫描的过程中都可以维持双端队列里保存的下标所对应

的A数组中元素从头到尾是递增的。同时扫描过程中如果i>m,那么我们同时也对双端队列的头部进行检测,如果头部元素的下表< i - m + 1的话,那么我们就

将其从头部删除。这样操作之后, 双端队列里保存的下标都在[i - m + 1, i]之间。同时双端队列的头部保存的下表对应的元素最小。那么就有B[i - m +1] =A[deque[head]]

这样整个扫描过程完成后,就计算出了所有的B[i]。 整个过程中我们对双端队列的操作的时间复杂度是O(n),因为每个元素最多进出一次。所以显然时间复杂度

是O(n).

2, 单调的双端队列在此题的应用。

等明晚把上面两项内容补上。 在这里占个位置先。

const int MAXN = 1e5 + 10;

int head, tail;
int deq[MAXN << 1];

int n;
int data[MAXN << 1], sum[MAXN << 1];

class Solution {
public:
int canCompleteCircuit(vector<int> &gas, vector<int> &cost) {
// Note: The Solution object is instantiated only once and is reused by each test case.
n = gas.size();
for(int i = 0; i < n; ++ i)
data[i + 1] = gas[i] - cost[i];
for(int i = 1; i < n; ++ i)
data[n + i] = data[i];
sum[0] = 0;
for(int i = 1; i < (n << 1); ++ i)
sum[i] = sum[i - 1] + data[i];
head = tail = 0;
vector<int> ret;
for(int i = 1; i < (n << 1); ++ i){
while(head != tail)
if(sum[deq[tail - 1]] >= sum[i]) -- tail; else break;
deq[tail ++ ] = i;
while(head != tail)
if(deq[head] < i - n + 1) ++ head; else break;
if(i >= n){
if(sum[deq[head]] >= sum[i - n])
ret.push_back(i - n + 1);
}
}
if(ret.size() <= 0)
return -1;
else
return ret[0] - 1;
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: