1. 程式人生 > 實用技巧 >客戶的一個緊急bug,我用了兩種方式進行 C# 反編譯修改原始碼

客戶的一個緊急bug,我用了兩種方式進行 C# 反編譯修改原始碼

LeetCode 134 加油站

https://leetcode-cn.com/problems/gas-station/

法一 直接模擬

從頭到尾遍歷每個加油站,檢查以該加油站為起點,最終能否繞行一圈。

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int sz = gas.size();
        for (int start = 0; start < sz; ++start) {
            if (gas[start] < cost[start]) continue;
            int v = gas[start];         // 剩餘汽油量
            int j = (start + 1) % sz;   // 指向下一個要到達的站點
            while (j != start) {
                // 到達站點j並加油後的剩餘汽油量
                v = v - cost[(j - 1 + sz) % sz] + gas[j];
                // 如果無法達到下一個(j+1)個站點
                if (v < cost[j]) break;
                j = (j + 1) % sz;
            }
            if (j == start) return start;
        }

        return -1;
    }
};

法二 一次遍歷

我們嘗試對上面的法一進行加速。假設從站點\(x\)出發,每經過一個加油站就加一次油,第一個無法到達的站點是\(y\)力扣官方題解上應該是有誤,如果第一個無法到達的站點是\(y\),那麼下面公式的上標只能取到\(y-1\),而不是\(y\)),則有以下公式成立:

\[\sum_{i = x}^{y-1} gas[i] \lt \sum_{i = x}^{y-1} cost[i] \]

\[\sum_{i = x}^j gas[i] >= \sum_{i = x}^{j}cost[i], x \le j \le y - 2 \]

第一個式子表示無法到達站點\(y\),第二個式子表明可以到達\(y\)

之前的所有站點。現在,考慮任意一個位於\(x,y\)之間的加油站\(z\),考察從該加油站出發能否到達站點\(y\),也就是要判斷\(\sum_{i = z}^{y-1} gas[i]\)\(\sum_{i = z}^{y - 1} cost[i]\)。根據上面的式子我們可以得到:

\[\begin{equation} \begin{aligned} \sum_{i = z}^{y - 1} gas[i] &= \sum_{i = x}^{y - 1} gas[i] - \sum_{i = x}^{z - 1} gas[i]\\ & \lt \sum_{i = z}^{y - 1} cost[i] - \sum_{i = x}^{z - 1} gas[i] \\ & \lt \sum_{i = z}^{y - 1} cost[i] - \sum_{i = x}^{z - 1} cost[i]\\ & = \sum_{i = z}^{y - 1} cost[i] \end{aligned} \end{equation} \]

以上公式說明從\(x, y\)之間的任何一個站點出發,都無法到達站點\(y\)。既然如此,我們首先檢查第0個加油站,並試圖找到第一個無法到達的加油站\(z\)。如果能找到,下一次就從加油站\(z + 1\)開始檢查。最終,我們只遍歷了原陣列一次。時間複雜度降到了\(O(N)\)

程式碼如下:

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int sz = gas.size();
        int start = 0;
        while (start < sz) {
            // 從起始位置開始往前移動
            int skip = 0;
            int sumOfGas = 0, sumOfCost = 0;
            while (skip < sz) {
                int z = (start + skip) % sz;
                sumOfGas += gas[z];
                sumOfCost += cost[z];
                // 如果找到了第一個無法到達的站點x則退出
                if (sumOfCost > sumOfGas) break;
                ++skip;
            }

            // 如果剛好繞了一圈回到了起點則返回起點位置下標
            if (skip == sz) return start;
            // 否則將起始位置調整為x + 1
            start += (skip + 1);
        }

        return -1;
    }
};