leetcode演算法之數組

明年就是找工作了,又要開始刷題了,把之前做過的題目再梳理整理一遍,但願明年不要那麼拉跨,祈禱明年能找到工作,千萬不能畢業就失業。

分類別解析leetcode上的一些相關的例題路,程式碼採用C++與python實現。

所有的題目均來自leetcode官網.

數組

涉及的一些leetcode問題:

  • 283 Move Zeroes (Easy)
  • 566 Reshape the Matrix (Easy)
  • 485 Max Consecutive Ones (Easy)
  • 240 Search a 2D Matrix II (Medium)
  • 378 Kth Smallest Element in a Sorted Matrix ((Medium))
  • 645 Set Mismatch (Easy)
  • 287 Find the Duplicate Number (Medium)
  • 697 Degree of an Array (Easy)
  • 766 Toeplitz Matrix (Easy)
  • 565 Array Nesting (Medium)

283 Move Zeroes (Easy)

給定一個數組 nums,編寫一個函數將所有 0 移動到數組的末尾,同時保持非零元素的相對順序。

說明:

  必須在原數組上操作,不能拷貝額外的數組。
  盡量減少操作次數。
  • 第一個直觀的想法:
    使用額外的數組,直接遍歷一遍原始數組,然後將非零的複製到前半部分,在後邊按照數組補零即可.
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
    // 新建一個vector,保存移動後的vector
        vector<int> res(nums.size(), 0);
        int j = 0;
        // 遍歷原始數組,修改res中的元素
        for (int i=0; i < nums.size(); i++){
            if (nums[i]!=0){
                res[j] = nums[i];
                j++;
            }
        }
        // 將res再賦值給原始數組
        nums.assign(res.begin(), res.end());
    }
};
  • 題目要求不能使用額外的數組.
    就只能使用雙指針在原始數組上操作,左指針指向交換完成的元素,右指針向後掃描查找交換元素
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        // 雙指針
        int l=0, r=0;

        while (r < nums.size()){
            if (nums[r] != 0){
                swap(nums[r], nums[l]);
                l++;
            }
            r++;
        }
    }
};

566 Reshape the Matrix (Easy)

題目較簡單,直接附上程式碼,遍歷即可

class Solution {
public:
    vector<vector<int>> matrixReshape(vector<vector<int>>& nums, int r, int c) {
        // 直接遍歷即可
        int m=nums.size(), n=nums[0].size();
        if (m*n != r*c) return nums;
        vector<vector<int>> res(r, vector<int>(c, 0)); //保存最終的結果
        for (int i = 0; i < m; i++){
            for (int j = 0; j < n; j++){
                int index = i*n + j;
                res[index/c][index%c] = nums[i][j];
            }
        }
        return res; 
    }
};

485 Max Consecutive Ones (Easy)

class Solution {
public:
    int findMaxConsecutiveOnes(vector<int>& nums) {
        //直接遍歷
        int res = 0;
        int i = 0;
        while (i < nums.size()){
            int count = 0;
            while (i < nums.size() && nums[i] == 1){
                count++;
                i++;
            }
            res = max(res, count);
            i++;
        }
        return res;
    }
};

240 Search a 2D Matrix II (Medium)

編寫一個高效的演算法來搜索 m x n 矩陣 matrix 中的一個目標值 target 。該矩陣具有以下特性:

每行的元素從左到右升序排列。
每列的元素從上到下升序排列。

  • 最直觀的想法, 直接遍歷二維數組進行對比,這就變成了一道簡單題,沒有用到題目當中的條件

  • 從右上角開始遍歷,因為這樣遍歷,比target小的在左邊,比target大的在下邊

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        // 從右上角開始遍歷
        int m = matrix.size(), n = matrix[0].size();
        int i = 0, j = n-1;
        while (i >= 0 && i < m && j >= 0 && j < n){
                if (matrix[i][j] == target) 
                    return true;
                else if (matrix[i][j] > target)
                    j--;
                else 
                    i++;
        }
        return false;
    }
};

378 Kth Smallest Element in a Sorted Matrix ((Medium))

給定一個 n x n 矩陣,其中每行和每列元素均按升序排序,找到矩陣中第 k 小的元素。

  • 第一個思路: 直接將其變為一維數組,然後直接進行排序,找到第k小的元素.
class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        int m = matrix.size(), n = matrix[0].size();
        vector<int> res(m*n, 0);
        int index = 0;
        for (int i = 0; i < m; i++){
            for (int j = 0; j < n; j++){
                res[index] = matrix[i][j];
                index++;
            }
        }
        sort(res.begin(), res.end());
        return res[k-1];
    }
};
  • 第二個思路,利用優先順序隊列.

將所有的元素全部插入優先順序隊列中,然後找到第k個

class point{
    public:
        int val, x, y;
        point()=default;
        point(int val, int x, int y): val(val), x(x), y(y) {}
        bool operator > (const point& a) const {
            return this->val > a.val;
        }
};

class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        //按行進行歸併排序
        //定義優先順序隊列,這個隊列採用vector實現
        priority_queue<point, vector<point>, greater<point>> q;
        int n = matrix.size();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++)
            q.emplace(point(matrix[i][j], i, j));
        }
        point res;
        for (int i = 0; i < k - 1; i++) {
            res = q.top();
            q.pop();
        }
        return q.top().val;
    }
};

插入的時候不需要將所有的元素均插入,按照其升序的特點,只需要按行插入右側元素即可,找到最小的k個

class point{
    public:
        int val, x, y;
        point()=default;
        point(int val, int x, int y): val(val), x(x), y(y) {}
        bool operator > (const point& a) const {
            return this->val > a.val;
        }
};

class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        //按行進行歸併排序
        //定義優先順序隊列,這個隊列採用vector實現
        priority_queue<point, vector<point>, greater<point>> q;
        int n = matrix.size();
        for (int i = 0; i < n; i++) {
            q.emplace(point(matrix[i][0], i, 0));
        }
        for (int i = 0; i < k - 1; i++) {
            point now = q.top();
            q.pop();
            if (now.y != n - 1) {
                q.emplace(matrix[now.x][now.y + 1], now.x, now.y + 1);
            }
        }
        return q.top().val;
    }
};

645 Set Mismatch (Easy)

集合 S 包含從1到 n的整數。不幸的是,因為數據錯誤,導致集合裡面某一個元素複製了成了集合裡面的另外一個元素的值,導致集合丟失了一個整數並且有一個元素重複。

給定一個數組 nums 代表了集合 S 發生錯誤後的結果。你的任務是首先尋找到重複出現的整數,再找到丟失的整數,將它們以數組的形式返回。

非常簡單的題目, 直接使用哈希表即可,找出哪一個數字已經出現過,然後查找那個數字沒有出現過,共遍歷兩次.

class Solution {
public:
    vector<int> findErrorNums(vector<int>& nums) {
        vector<int> res;
        unordered_set<int> hashset;
        for (auto c : nums){
            if (hashset.find(c) == hashset.end())
                hashset.insert(c);
            else 
                res.push_back(c);
        }
        for (int i=1; i <= nums.size(); i++){
            if (hashset.find(i) == hashset.end())
                res.push_back(i);
        }
        return res;
    }
};

287 Find the Duplicate Number (Medium)

給定一個包含 n + 1 個整數的數組 nums,其數字都在 1 到 n 之間(包括 1 和n),
可知至少存在一個重複的整數。假設只有一個重複的整數,找出這個重複的數。

直觀的幾種想法:

  • 直接遍歷,兩重循環,每次查找元素nums[i]是否在nums[i+1:]的範圍內出現,時間複雜度n2,不滿足題意
  • hash表存儲訪問過的元素,空間複雜度o(n),不滿足題意
  • 先排序,然後遍歷相鄰的相同元素即為重複元素,不滿足”不能更改原數組”的要求.

這道題的限制條件,直接堵死了常用的幾種直觀的思路.

這裡採用二分查找來解決這個問題.

題解:

  1. 這道題的數字的範圍均在1~n之間,且只有一個重複的數.舉個例子就知道
    例如 [1,3,4,2,2], n=4,因此設置mid = (1+4) / 2=2, 那麼小於等於2的數字有3個大於選定的mid,因此重複的數字就肯定小於等於mid,否則重複的數字大於mid
class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int n = nums.size()-1;
        int l= 0, r = n;
        // 二分法查找區間
        while (l < r){
            int mid = l + (r - l) / 2;
            int cnt = 0;
            // 統計小於等於mid的數字的數目
            for (auto num :nums){
                if (num <= mid)
                    cnt++;
            }
            // 如果統計的數字大於mid,那麼最終重複的數字位於左側
            if (cnt > mid)
                r = mid;
            else
                l = mid+1;
        }
        return l;
    }
};

697 Degree of an Array (Easy)

給定一個非空且只包含非負數的整數數組 nums, 數組的度的定義是指數組裡任一元素出現頻數的最大值。

你的任務是找到與 nums 擁有相同大小的度的最短連續子數組,返回其長度。

簡單的題目,但是注意頻數最大的數組元素不一定只有一個

#include <algorithm>

class Solution {
public:
    int findShortestSubArray(vector<int>& nums) {
        // 使用無序映射表來存儲每個元素在數組中出現的索引列表
        unordered_map<int, vector<int>> res;
        for (auto e : nums){
            res.insert(make_pair(e, vector<int>()));
        }

        for (int i=0; i <nums.size(); i++){
            res[nums[i]].push_back(i);
        }
        
        int min_dis = nums.size();
        int max_ferq = 0;
        // 查找出現最多的索引出現的次數
        for (auto e=res.cbegin(); e != res.end(); e++){
            vector<int> index = e->second;
            int dis = index.size();
            max_ferq = max(max_ferq, dis);
        }
        //統計最小的連續子數組
        for (auto e=res.cbegin(); e != res.end(); e++){
            vector<int> index = e->second;
            if (index.size() == max_ferq){
                min_dis = min(min_dis, index[index.size()-1] - index[0] + 1);
            }
        }
        return min_dis;
    }
};

766 Toeplitz Matrix (Easy)

如果矩陣上每一條由左上到右下的對角線上的元素都相同,那麼這個矩陣是 托普利茨矩陣 。

給定一個 M x N 的矩陣,當且僅當它是托普利茨矩陣時返回 True。

簡單的題目, 僅僅需要發現同一條對角線上的橫縱坐標差值為常數(在做八皇后問題也會遇到)就可以了,然後藉助map可以輕鬆求解.

class Solution {
public:
    bool isToeplitzMatrix(vector<vector<int>>& matrix) {
        // 利用哈希表來,鍵為行列坐標的差值, 值為對應的數字
        unordered_map<int, int> indexdiff_value;
        for (int i = 0; i < matrix.size(); i++){
            for (int j = 0; j < matrix[0].size(); j++){
                if (indexdiff_value.find(i - j) == indexdiff_value.end())
                    indexdiff_value.insert(make_pair(i-j, matrix[i][j]));
                else{
                    if (matrix[i][j] != indexdiff_value[i-j])
                        return false;
                }
            }
        }
        return true;
    }
};

565 Array Nesting (Medium)

索引從0開始長度為N的數組A,包含0到N – 1的所有整數。找到最大的集合S並返回其大小,其中 S[i] = {A[i], A[A[i]],
A[A[A[i]]], … }且遵守以下的規則。

假設選擇索引為i的元素A[i]為S的第一個元素,S的下一個元素應該是A[A[i]],之後是A[A[A[i]]]…
以此類推,不斷添加直到S出現重複的元素。

看似複雜,其實只需要將所有的 這種嵌套的子列全部查找出來,並找到最長的滿足條件的最長的長度即可.

例如,對於[5,4,0,3,1,6,2]這個序列,其滿足條件的S分別為: [5, 6, 0, 2], [4,1]與[3]這三個從中找到最短的即可.

基本辦法就是遍歷+ hash表去重

class Solution {
public:
    int arrayNesting(vector<int>& nums) {
        //利用哈希表記錄訪問過的元素,避免重複訪問.
        unordered_set<int> visited;

        int res = 0; // 最終的結果,最長的S
        // 遍歷,找到所有的字串
        for (int i = 0; i < nums.size(); i++){
            if( visited.find(nums[i]) != visited.end() ) continue;

            int length = 1;  // S的長度
            // 開始查找一個滿足條件的S.
            int index = i;
            while (nums[index] != i){
                visited.insert(nums[index]);
                index = nums[index];
                length++;
            }
            visited.insert(nums[i]);
            res = max(res, length);
        }
        return res;
    }
};

更多筆記資料

Tags: