1.拿到题目,想到本题应该是在“gas[i] – cost[i]”(即净赚汽油)上做文章,所以尝试使用滑动窗口方法,不断向右移动右边界cur。当窗口内的净赚汽油总和大于0时说明能到达该处,如果小于0则不断收缩左边界start,直到总和大于0。同时维护已经途径的加油站个数step,当step == gasSize时说明找到了答案;如果cur已经到达数组末尾但还没有满足条件的step则说明无解。
2.基于以上思想,写出的完整代码如下:
1. int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize) { 2. // start:候选起点下标 3. int start = 0; 4. // cur:当前遍历到的站点指针 5. int cur = 0; 6. // sum:当前区间内剩余油量总和 7. int sum = 0; 8. // step:当前区间包含的站点数量 9. int step = 0; 10. 11. // 候选起点不超过总站数时循环 12. while (start < gasSize){ 13. // 计算当前站点加油减耗油的净油量 14. int tmp = gas[cur % gasSize] - cost[cur % costSize]; 15. sum += tmp; 16. // 前进到下一站 17. cur++; 18. step++; 19. 20. // 当前区间油量不足,不断抛弃起点站点,直到sum>=0或区间无站点 21. while (sum < 0 && step > 0){ 22. sum -= gas[start % gasSize] - cost[start % costSize]; 23. start++; 24. step--; 25. } 26. 27. // 区间站点数量等于总站数,说明成功绕环一周,返回起点 28. if (step == gasSize) return start; 29. } 30. 31. // 所有起点均无法完成环路,返回-1 32. return -1; 33. }该算法时间复杂度为O(n),空间复杂度为O(1)。
3.在使用滑动窗口的时候遇到了如下细节问题:
(1)一开始错误地认为净赚汽油为gas[cur % gasSize] - cost[(cur + gasSize - 1) % costSize],原因是看到官方题目示例中的一加一减就是错位的:
但没有考虑该示例一开始就单独加上了出发地的获取汽油数,并在最后单独减去了到达该处的所需汽油数,所以真正的周期还是gas[cur % gasSize] - cost[cur % costSize],即当前加油站能获取的汽油数减去到达当前加油站的所需汽油数。
(2)收缩左边界start应该放在while循环中而不是if中,因为本题滑动窗口的逻辑需要内部总和大于0时再扩张右边界cur,只if判断一次、收缩左边界start一次无法保证内部总和大于0。