[array] leetcode - 54. Spiral Matrix - Medium

leetcode-54. Spiral Matrix - Medium

descrition

GGiven a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order.

For example,
Given the following matrix:

[
 [ 1, 2, 3 ],
 [ 4, 5, 6 ],
 [ 7, 8, 9 ]
]
You should return [1,2,3,6,9,8,7,4,5].

解析

基本思路是按照规则一个个的取数,两份方法的区别在于处理的技巧不同。

方法 1:

小技巧(si,sj),(ei, ej),对角线可以唯一确定一个矩阵。循环获取结果,每次循环获取当前最外层的一圈数字。

  1. 从左到右,matrix[si][sj,...,ej]
  2. 从上到下,matrix[si,...,ej][ej]
  3. 从右到左,matrix[ei][ej-1,...,sj]
  4. 从下到上,matrix[ei-1,...,si-1][sj]

实现如代码所示,注意边界条件。

方法 2:

我们只需要模拟时钟的顺时针行走,每走一步获取一个元素,假设矩阵 matrix 有 m 行 n 列,那么只需要循环 m * n 次即可获取所有元素。算法描述如下:

定义两个辅助数组变量如下,dr 和 dc 分别表示行和列的方向变化。

dr={0, 1, 0, -1}
dc={1, 0, -1, 0}  

两个数组共同组合有以下 4 种情况:

  1. dr[0], dc[0]:行的增量为 0,列的增量为 1,对应方法 1 的情况 1
  2. dr[1], dc[1]:行的增量为 1,列的增量为 0,对应方法 1 的情况 2
  3. dr[2], dc[2]:行的增量为 0,列的增量为 -1, 对应方法 1 的情况 3
  4. dr[3], dc[3]:行的增量为 -1,列的增量为 0,对应方法 1 的情况 4

我们总共遍历 m * n 次,每次获取一个元素,而没个元素的状态都将是以上 4 种状态中的一种。令 r,c 分别表示当前的行和列的下标,cr, cc 分别表示 candidate,即下一个候选位置的下标,di 表示当前的方向(对应以上 4 种情况,即 dr,dc 的下标),visited[][] 对应 matrix,表示元素是否被访问过。

对于任意一次遍历,若 r,c 合法,即没有超出矩阵边界又没有被访问过,那么我们可以获取当前元素。否则,我们需要调换方向,即更新 di 的值,具体实现参考代码。

实际上方法 2 不是最优解,相比于方法 1, 方法 2 需要一个辅助的数组,但其思想非常值得借鉴,尤其是状态控制的 dr[] 和 dc[] ,di

leetcode-solution

code

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

class Solution{
public:
    vector<int> spiralOrder(vector<vector<int> >& matrix){

        //return spiralOrderCycle(matrix);
        return spiralOrderSimilation(matrix);

    }


    // time-O(m*n), space-O(1) exclude the space used to return info.
    vector<int> spiralOrderCycle(vector<vector<int> >& matrix){
        vector<int> ans;
        if(matrix.empty())
            return ans;

        int rows = matrix.size(), cols = matrix[0].size();
        int si = 0, sj = 0;
        int ei = rows - 1, ej = cols - 1;

        while(si <= ei && sj<=ej){
            spiralOrderOneCycle(matrix, si, sj, ei, ej, ans);
            si++;
            sj++;
            ei--;
            ej--;
        }

        return ans;
    }

    void spiralOrderOneCycle(vector<vector<int> >& matrix, 
                             int si, int sj, int ei, int ej,
                             vector<int>& ans){
        // top row
        for(int j=sj; j<=ej; j++)
            ans.push_back(matrix[si][j]);

        // right column
        for(int i=si+1; i<=ei; i++)
            ans.push_back(matrix[i][ej]);

        // down row if exist
        if(si < ei){
            for(int j=ej-1; j>=sj; j--)
                ans.push_back(matrix[ei][j]);
        }

        // left column if exist
        if(sj < ej){
            for(int i=ei-1; i>si; i--)
                ans.push_back(matrix[i][sj]);
        }
    }

    // time-O(m*n), space=O(m*n)
    vector<int> spiralOrderSimilation(vector<vector<int> > &matrix){
        vector<int> ans;
        if(matrix.empty())
            return ans;
        int rows = matrix.size(), cols = matrix[0].size();
        vector<vector<bool> > visited(rows, vector<bool>(cols, false));
        int dr[] = {0, 1, 0, -1};
        int dc[] = {1, 0, -1, 0};
        // start from the left-top corner (r,c) = (0,0) and from left to right
        // di = 0, dr[0] and dc[0], correspond left to right.
        int di = 0, r = 0, c = 0; 
        
        for(int i=0; i<rows*cols; i++){
            ans.push_back(matrix[r][c]);
            visited[r][c] = true;
            int cr = r + dr[di];
            int cc = c + dc[di];
            if(0 <= cr && cr < rows && 0 <= cc && cc < cols && !visited[cr][cc]){
                r = cr;
                c = cc; 
            }else{
                // change direction
                di = (di + 1) % 4;
                r = r + dr[di];
                c = c + dc[di];
            }
        }

        return ans;
    }
};

int main()
{
    return 0;
}

转载于:https://www.cnblogs.com/fanling999/p/7882056.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值