標籤:加油站 演算法 面試 java
【134-Gas Station(加油站問題】
【LeetCode-面試演算法經典-Java實現】【所有題目目錄索引】
原題
There are N gas stations along a circular route, where the amount of gas at station i is gas[i].
You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from station i to its next station (i+1). You begin the journey with an empty tank at one of the gas stations.
Return the starting gas station’s index if you can travel around the circuit once, otherwise return -1.
Note:
The solution is guaranteed to be unique.
題目大意
沿環形路線有N個加油站,其中氣體在車站i是量是gas[i]。你有車有無限容量的氣罐,從加油站i到下一個加油站網站i+1,要消耗cost[i]的氣體。你開始旅程時,氣罐是空的。回到起始加油站的指數,選擇一個起點開始旅遊,如果你能在周圍環形旅行一次,就返回開始的加油站索引,否則返回-1。
注意: 答案保證是唯一的。
解題思路
假設從網站 i 出發,到達網站 k 之前,依然能保證油箱裡油沒見底兒,從k 出發後,見底兒了。那麼就說明 diff[i] + diff[i+1] + … + diff[k] < 0,而除掉diff[k]以外,從diff[i]開始的累加都是 >= 0的。也就是說diff[i] 也是 >= 0的,這個時候我們還有必要從網站 i + 1嘗試嗎?仔細一想就知道:車要是從網站 i+1出發,到達網站k後,甚至還沒到網站k,油箱就見底兒了,因為少加了網站 i 的油。。。
因此,當我們發現到達k 網站郵箱見底兒後,i 到 k 這些網站都不用作為出發點來實驗了,肯定不滿足條件,只需要從k+1網站嘗試即可!因此解法時間複雜度從O(n2)降到了 O(2n)。之所以是O(2n),是因為將k+1站作為始發站,車得繞圈開回k,來驗證k+1是否滿足。
等等,真的需要這樣嗎?
我們類比一下過程:
a. 最開始,網站0是始發站,假設車開出網站p後,油箱空了,假設sum1 = diff[0] +diff[1] + … + diff[p],可知sum1 < 0;
b. 根據上面的論述,我們將p+1作為始發站,開出q站後,油箱又空了, 設sum2 = diff[p+1] +diff[p+2] + … + diff[q],可知sum2 < 0。
c. 將q+1作為始發站,假設一直開到了未迴圈的最末站,油箱沒見底兒,設sum3 = diff[q+1] +diff[q+2] + … + diff[size-1],可知sum3 >= 0。
要想知道車能否開回 q 站,其實就是在sum3 的基礎上,依次加上 diff[0] 到 diff[q],看看sum3在這個過程中是否會小於0。但是我們之前已經知道 diff[0] 到 diff[p-1] 這段路,油箱能一直保持非負,因此我們只要算算sum3 + sum1是否 <0,就知道能不能開到 p+1站了。
如果能從p+1站開出,只要算算sum3 + sum1 + sum2 是否 < 0,就知都能不能開回q站了。
因為 sum1, sum2 都 < 0,因此如果 sum3 + sum1 + sum2 >=0 那麼sum3 + sum1 必然 >= 0,也就是說,只要sum3 + sum1 + sum2 >=0,車必然能開回q站。而sum3 + sum1 + sum2 其實就是 diff數組的總和 Total,遍曆完所有元素已經算出來了。
因此 Total 能否 >= 0,就是是否存在這樣的網站的 充分必要條件。
這樣時間複雜度進一步從O(2n)降到了 O(n)。
代碼實現
演算法實作類別
public class Solution { public int canCompleteCircuit(int[] gas, int[] cost) { // 參數檢驗 if (gas == null || cost == null || gas.length == 0 || gas.length != cost.length) { return -1; } // 記錄訪問的起始點 int start = 0; // 加的氣和消耗的氣的總差值 int total = 0; // 從start位置開始,加的氣和消耗的氣的總差值 int sum = 0; for (int i = 0; i < gas.length; i++) { total += (gas[i] - cost[i]); // 如是油箱沒有油了 if (sum < 0) { // 重新設定油箱中的油 sum = gas[i] - cost[i]; // 記錄新的起點位置 start = i; } else { // 油箱中還有油,更新油箱中的油數 sum += (gas[i] - cost[i]); } } return total >= 0 ? start : -1; } // 下面的方法會逾時O(N^2)時間複雜度 public int canCompleteCircuit2(int[] gas, int[] cost) { // 參數檢驗 if (gas == null || cost == null || gas.length == 0 || gas.length != cost.length) { return -1; } // 剩下的氣體,開始時為0 int leftGas = 0; // 開始出發的網站 int start = 0; // 結束的網站 int end = 1; // 未走一周 while (start < gas.length) { // 到達下一個站後的氣體簡便量 leftGas = gas[start] - cost[start]; // 可以走到下一個站 if (leftGas > 0) { // 記錄下一個站 end = (start + 1) % gas.length; // 如果一直可以到下一個站就持續進行操作 while (start != end && (leftGas += (gas[end] - cost[end])) >= 0) { end = (end + 1) % gas.length; } // 說明已經遍曆了一周 if (start == end) { return start; } } start++; } return -1; }}
評測結果
點擊圖片,滑鼠不釋放,拖動一段位置,釋放後在新的視窗中查看完整圖片。
特別說明
歡迎轉載,轉載請註明出處【http://blog.csdn.net/derrantcm/article/details/47678215】
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
【LeetCode-面試演算法經典-Java實現】【134-Gas Station(加油站問題)】