【程式碼隨想錄】一、陣列:5.螺旋矩陣

henFiu發表於2024-08-15

本題並不涉及到什麼演算法,就是模擬過程,但卻十分考察對程式碼的掌控能力。

1.題目1:59. 螺旋矩陣 II

1.1.解法1:模擬

本題的重點還是像之前的“704.二分查詢”,堅持迴圈不變數原則,即在本題中遍歷每條邊時,堅持相同的原則

如下是一個示例,即n=5,我們考慮根據圈數和邊數來進行遍歷:
由外圈到內圈,而邊的方向則為:①從左到右、②從上到下、③從右到左、④從下到上。
(1)從外圈到內圈:引入start變數作為“轉圈”開始的位置,初始時start的值為0;第二圈時,start的值為1。
(2)邊①②③④:這裡在遍歷每條邊時,始終堅持“左閉右開”,也就是遍歷①時1、2、3、4,遍歷②時5、6、7、8,遍歷③時9、10、11、12,遍歷④時13、14、15、16。透過offset來控制每條邊遍歷結束的位置(n-offset),相應第二圈時,offset的值+1。
(3)當n為奇數時,中間會剩下一個數,如圖中“25”,我們最後對中間數進行單獨賦值。
image

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>>nums(n, vector<int>(n, 0));
        int loop = n / 2; // 要旋轉的圈數
        int start = 0; // 起始位置
        int offset = 1; // 控制每一條邊遍歷的長度,每次迴圈右邊界收縮一位
        int count = 1; // 用於賦值
        while (loop--) {
            int j = start, i = start;
            // 以下四個for迴圈即旋轉了一圈
            // (1)從左向右
            for (; j < n - offset; j++) {
                nums[i][j] = count++;
            }
            // (2)從上向下
            for (; i < n - offset; i++) {
                nums[i][j] = count++;
            }
            // (3)從右向左
            for (; j > start; j--) {
                nums[i][j] = count++;
            }
            // (4)從下向上
            for (; i > start; i--) {
                nums[i][j] = count++;
            }
            start++; // 第二圈開始的時,起始位置要加1
            offset += 1; // 控制每一圈裡每一條邊遍歷的長度
        }
        // 如果n為奇數的話,需要單獨給矩陣最中間的位置賦值
        if (n % 2 == 1) nums[n / 2][n / 2] = count;
        return nums;
    }
};

● 時間複雜度:\(O(n^2)\)
● 空間複雜度:\(O(1)\)

2.題目2:54.螺旋矩陣

類似於59.螺旋矩陣II,但本題矩陣行列不一定相等,且要求順時針方向輸出指定的矩陣。

2.1.解法1:模擬

思路上和59.螺旋矩陣II相似,都是:由外圈到內圈,而邊的方向則為:①從左到右、②從上到下、③從右到左、④從下到上。
不過需要注意一點,因為行數列數不相等的情況下:
1.旋轉的圈數應為:min(列數,行數) / 2。
2.當min(列數,行數)為奇數時:
min(列數,行數) = 列數,即還剩餘一箇中間列沒有被遍歷到;
min(列數,行數) = 行數,即還剩餘一箇中間行沒有被遍歷到;
剩餘的中間行或是中間列還需要單獨去進行遍歷,按照題意填充到目標陣列中。

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        vector<int>nums(m * n);
        int start = 0;
        int loop = min(m, n) / 2;
        int count = 0;
        int offset = 1;
        int i, j;
        while (loop --) {
            i = start, j = start;
            // (1)從左往右
            for (; j < n - offset; j++) {
                nums[count++] = matrix[i][j];
            }
            // (2)從上往下
            for (; i < m - offset; i++) {
                nums[count++] = matrix[i][j];
            }
            // (3)從右往左
            for (; j > start; j--) {
                nums[count++] = matrix[i][j];
            }
            // (4)從下往上
            for (; i > start; i--) {
                nums[count++] = matrix[i][j];
            }
            start++;
            offset++;
        }

        int mid = min(m, n) / 2;
        if (min(m, n) % 2) {
            if (m > n) { // 剩下一個中間列
                for (int i = mid; i < mid + m - n + 1; i++) {
                    nums[count++] = matrix[i][mid];
                }
            }
            else { // 剩下一個中間行
                for (int i = mid; i < mid + n - m + 1; i++) {
                    nums[count++] = matrix[mid][i];
                }                
            }
        }
        return nums;
    }
};

● 時間複雜度:\(O(n^2)\) 模擬遍歷二維矩陣的時間。
● 空間複雜度:\(O(1)\)

2.2.解法2:模擬(簡潔)

這是力扣上某大佬的一種解法
1.設定上下左右邊界。
2.從左向右,第一行已經遍歷完,便可以從中刪除,相當於重新定義上邊界。
3.如果重新定義的上邊界與下邊界交錯,表示遍歷結束,跳出迴圈,返回答案。
若不交錯,繼續向下、向左、向上遍歷,操作步驟同1.2。
4.迴圈以上步驟,直至兩邊界交錯,跳出迴圈,返回答案。

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int>ans;
        if (matrix.empty()) return ans;
        int left = 0, right = matrix[0].size() - 1, top = 0, bottom = matrix.size() - 1;
        while (true) {
            // 1.從左向右
            for (int i = left; i <= right; i++) {
                ans.push_back(matrix[top][i]);
            }
            if (++top > bottom) break;
            // 2.從上向下
            for (int i = top; i <= bottom; i++) {
                ans.push_back(matrix[i][right]);
            }
            if (--right < left) break;
            // 3.從右向左
            for (int i = right; i >= left; i--) {
                ans.push_back(matrix[bottom][i]);
            }
            if (--bottom < top) break;
            // 4.從下向上
            for (int i = bottom; i >= top; i--) {
                ans.push_back(matrix[i][left]);
            }
            if (++left > right) break;
        }
        return ans;
    }
};

3.題目3:LCR 146. 螺旋遍歷二維陣列

這題的解法同54。

3.1.解法1:模擬

class Solution {
public:
    vector<int> spiralArray(vector<vector<int>>& array) {
        if (array.size() == 0 || array[0].size() == 0) return {};
        int m = array.size(), n = array[0].size();
        vector<int>nums(m * n);
        int loop = min(m, n) / 2;
        int start = 0;
        int offset = 1;
        int count = 0;
        int i, j;
        while (loop --) {
            i = start, j = start;
            for (; j < n - offset; j++) {
                nums[count++] = array[i][j];
            }
            for (; i < m - offset; i++) {
                nums[count++] = array[i][j];
            }
            for (; j > start; j--) {
                nums[count++] = array[i][j];
            }
            for (; i > start; i--) {
                nums[count++] = array[i][j];
            }
            start++;
            offset++;            
        }

        int mid = min(m, n) / 2;
        if (min(m, n) % 2) {
            if (m > n) {
                for (int i = mid; i < mid + m - n + 1; i++) {
                    nums[count++] = array[i][mid];
                }
            }
            else {
                for (int i = mid; i < mid + n - m + 1; i++) {
                    nums[count++] = array[mid][i];
                }
            }
        }
        return nums;
    }
};

● 時間複雜度:\(O(n^2)\) 模擬遍歷二維矩陣的時間。
● 空間複雜度:\(O(1)\)

相關文章