1. 程式人生 > >【LeetCode】Array Topic總結

【LeetCode】Array Topic總結

/*基本思路就是1位1位往後探測,看當前位置往前的所有位是否能夠組成滿足要求的字串
因為最後一位是0,所以只要判斷前n-1位能否組成滿足要求的字串即可*/
class Solution {
public:
    bool isOneBitCharacter(vector<int>& bits) {
        auto len = bits.size();
        if (len == 1) return true;
        vector<bool> vb(len, false); // vb記錄當前位置及之前的位能夠組成字串
        // 為vb[0]和vb[1]賦初值,簡單分析一下就知道取值情況
vb[0] = 1 - bits[0]; if (bits[1] == 0) vb[1] = true; else vb[1] = bits[0]; for (int i = 2; i < len; ++i) { if (bits[i]) { // 當前位是1,則前一位必須也為1,同時前i-2位能組成字串 vb[i] = (bits[i-1] == 1) && vb[i-2]; } else { // 當前位是0,則前一位為1,同時前i-2位能組成字串,或者前i-1位能組成字串
vb[i] = vb[i-1] || ((bits[i-1] == 1) && vb[i-2]); } } // 判斷前n-1個位能夠組成字串,即為我們的答案 return vb[len-2]; } };
/*遞迴。只要找到1的位置,就進行深搜,找到所有相鄰的1,同時記錄1的個數,即為島的大小*/
class Solution {
public:
    // x,y是1的位置,res記錄1的個數,r,c為grid的行數和列數
    void depth(vector
<vector<int>>
& grid, int x, int y, int &res, const int& r, const int& c) { ++res; // 記錄1的個數 grid[x][y] = 0; // 標記訪問,將當前位置清0 if (x-1 >= 0 && grid[x-1][y] == 1) depth(grid, x-1, y, res, r, c); if (x+1 < r && grid[x+1][y] == 1) depth(grid, x+1, y, res, r, c); if (y-1 >= 0 && grid[x][y-1] == 1) depth(grid, x, y-1, res, r, c); if (y+1 < c && grid[x][y+1] == 1) depth(grid, x, y+1, res, r, c); } int maxAreaOfIsland(vector<vector<int>>& grid) { int r = grid.size(); if (r == 0) return 0; int c = grid[0].size(); int maxres = 0; for (int i = 0; i < r; ++i) { for (int j = 0; j < c; ++j) { if (grid[i][j]) { // 找到一個1,進行深搜 int t = 0; depth(grid, i, j, t, r, c); if (t > maxres) maxres = t; // 更新最大值 } } } return maxres; } };
/*題目讓構造一個1到n的排列,且相鄰兩個數差的絕對值正在能夠取遍1到k。
考慮如果前k+1個數正好能組成滿足要求的排列,後面的數按順序排列就好,因為他們的差絕對值為1。
考慮這樣一個排列,1,k+1,2,k,3,k-1,...,則差得絕對值正好為k,k-1,k-2,k-3,k-4,
即使用前1到k+1就能夠構造出滿足要求的排列*/
class Solution {
public:
    vector<int> constructArray(int n, int k) {
        vector<int> res;
        for (int i = 1; i <= k + 1; ++i) {
            // 接下面兩行判斷什麼時候結束構造
            if (i == k + 2 - i) { res.push_back(i); break; }
            if (i > k + 1 - i) break;
            res.push_back(i);
            res.push_back(k + 2 - i);
        }
        // 剩下的數按順序排列就好
        for (int i = k + 2; i <= n; ++i) res.push_back(i);
        return res;
    }
};
/*一個方法是使用字首和每個位置之前所有數的和,然後再遍歷每個區間,字首和相減得到區間和,
判斷是夠等於k,但這個方法的時間複雜度為O(n*n),太高了。
根據前面做題的經驗,關於求和的問題,通常會固定一個值,然後找另一個值。應用到本題,就是固定一個字首和,找到與其相差k的另一個字首和,具體看程式碼*/
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        // 特殊情況處理,本題其實不需要
        if (nums.size() == 0) return 0;
        if (nums.size() == 1) return (nums[0] == k) ? 1 : 0;
        // 使用map幫助查詢
        unordered_map<long long, int> m;
        int sumx = 0; // 記錄字首和
        int res = 0;
        ++m[0]; // 考慮區間是從第一個數開始的情況

        for (int i = 0; i < nums.size(); ++i) {
            sumx += nums[i];   // 記錄區間和
            res += m[sumx-k];  // 找到前面出現過的和為sumx-k的次數
            ++m[sumx];         // 更新map
        }
        return res;
    } 
};
/*讓找出出現次數超過n/2的數。考慮如果存在這樣的數,則它與其他數抵消之後,最後陣列中肯定只剩它自己,據此有以下程式碼*/
**class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int cnt = 0; // 當前數有幾個
        int res = nums[0];
        for (auto x : nums) {
            if (cnt == 0) { // 說明之前的數都抵消掉了
                res = x; // 記錄一個新數
                ++cnt;
            } else {
                if (x == res) ++cnt; // 更新當前數的次數
                else --cnt;      // 抵消掉
            }
        }
        return res;
    }
};
/* 動態規劃問題,考慮dp[n]表示以第n個數為結尾的最大和,則dp[n+1]只有兩種選擇,要麼=dp[n]+a[n+1],要麼=a[n+1],即=max(dp[n]+a[n+1], a[n+1]),空間複雜度和時間負責度都為O(n),空間複雜度可以優化到O(1) */
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int sumx = 0; // sumx記錄以當前數為結尾的最大和
        if (nums.size() == 0) return 0;
        int maxsum = nums[0];
        for (int x : nums) {
            sumx = max(sumx + x, x); // 更新,只與前一個狀態的最大和有關
            if (sumx > maxsum) maxsum = sumx;
        }
        return maxsum;
    }
};
/* 首先進行排序,然後選擇一條最大邊,只要剩下兩條邊的和比最大邊大就能組成三角形,由於已經有序,查詢起來很方便 */
class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        if (nums.size() < 3) return 0;
        sort(nums.begin(), nums.end()); // 排序
        int res = 0;
        int n = nums.size();
        for (int i = n-1; i >= 0; --i) {
            int j = 0, k = i - 1; // 最大邊位置i,剩下兩條位置為j和k
            while (j < k) {
                // 兩邊之和太小,只能增加最短邊
                if (nums[j] + nums[k] <= nums[i]) ++j; 
                // j滿足要求,則j到k-1的所有邊,都能和k和i兩條邊組成三角形,即找到了所有中間邊為nums[k]的情況
                // 然後考慮中間邊為nums[k-1]的情況,而j無需更新,
                else res += (k-j), --k;
            }
        }
        return res;
    }
};