编程题-螺旋遍历二维数组

题目:

给定一个二维数组 array,请返回「螺旋遍历」该数组的结果。

螺旋遍历:从左上角开始,按照 向右向下向左向上 的顺序 依次 提取元素,然后再进入内部一层重复相同的步骤,直到提取完所有元素。

解法一(模拟):

模拟打印二维数组的路径。初始位置是二维数组的左上角,初始方向是向右,当路径超出界限或者进入之前访问过的位置时,顺时针旋转,进入下一个方向。判断路径是否进入之前访问过的位置需要使用一个与输入二维数组大小相同的辅助visited,其中的每个元素表示该位置是否被访问过。当一个元素被访问时,将visited中的对应位置好元素设置为已访问。

由于二维数组中的每个元素都被访问一次,因此路径的长度即为二维数组中的元素数量,当路径的长度达到二维数组中的元素数量时即为完整路径,将该路径返回。

class Solution {
private:
    //创建四个螺旋遍历移动的顺序,{0,1}表示向右移动,{1,0}表示向下移动,{0,-1}表示向左移动,{-1,0}表示向上移动
    static constexpr int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
public:
    vector<int> spiralArray(vector<vector<int>>& array) {
        //判断array是否为空,符号其中一条即返回空vector的结果{}
        if (array.size() == 0 || array[0].size() == 0) {
            return {};
        }
        //计算二维数组的行列值为n和m
        int rows = array.size(), columns = array[0].size();
        //生成与array尺寸完全相同的bool类型的二维数组visited,用于记录数组哪些索引位置已访问和未访问
        vector<vector<bool>> visited(rows, vector<bool>(columns));
        //生成输出结果的vector<int>容器类型的order对象(total)表示容器中初始化的元素个数,初始值为0.
        int total = rows * columns;
        vector<int> order(total);

        int row = 0, column = 0;
        int directionIndex = 0;
        //跳出循环的依据是当order中元素都填满时,输出最终的结果
        for (int i = 0; i < total; i++) {
            order[i] = array[row][column];
            visited[row][column] = true;
            //计算下一个要遍历的元素的位置,nextRow表示行索引,nextColumn表示列索引
            int nextRow = row + directions[directionIndex][0], nextColumn = column + directions[directionIndex][1];
            //if条件语句判断,如果下一个行索引值小于0或大于等于行数(超出索引范围)或者下一个列索引值小于0或者大于等于列数(超出索引范围)或者下一个值已经被visited访问过
            //则ditectionIndex遵守遍历规则进行下一个顺序的遍历方向(向右->向下->向左->向上->向右)
            if (nextRow < 0 || nextRow >= rows || nextColumn < 0 || nextColumn >= columns || visited[nextRow][nextColumn]) {
                directionIndex = (directionIndex + 1) % 4;
            }
            //将正确的下一个要遍历的元素索引位置保存row,column并在下一个需要遍历的循环进行添加
            row += directions[directionIndex][0];
            column += directions[directionIndex][1];
        }
        return order;
    }
};

时间复杂度:O(mn),其中 m 和 n 分别是输入二维数组的行数和列数。二维数组中的每个元素都要被访问一次。空间复杂度:O(mn)。需要创建一个大小为 m×n 的二维数组 visited 记录每个位置是否被访问过。

解法二(按层模拟):

可以将二维数组看成若干层,首先打印最外层的元素,其次打印次外层的元素,直到打印最内层的元素。定义遍历层的上下左右四个索引位置up,down,left,right。当遍历完当前层的元素之后,将left和up分别增加1,将right和down分别减少1,进入下一层继续遍历,直到up=down或者left=right时跳出循环(此时为单条的向量,并不能构成闭环层)。随后再进行将剩下的单条向量进行最后的遍历,实现所有元素的螺旋遍历。如下为笔者代码:

class Solution {
public:
     void run_all(vector<vector<int>>& array, vector<int>& result, int up, int down, int left, int right){
        int i =left;
        int j =up+1;
        while(i<=right){
            result.push_back(array[up][i]);
            i++;
        }
        i--;
        i--;
        while(j<=down){
            result.push_back(array[j][right]);
            j++;
        }
        j--;
        while(i>=left){
            result.push_back(array[down][i]);
            i--;
        }
        i++;
        j--;
        while(j>up){
            result.push_back(array[j][left]);
            j--;
        }
    }

    vector<int> spiralArray(vector<vector<int>>& array) {
        vector<int> result;
        int n = array.size();
        if(n==0){
            return result;
        }
        int m = array[0].size();
        int up=0;
        int down=n-1;
        int left=0;
        int right=m-1;
        while(up<down and left<right){
            run_all(array,result,up,down,left,right);
            up++;
            down--;
            left++;
            right--;
        }
        if(up==down and left!=right){
            for(int i=left;i<=right;i++){
                result.push_back(array[up][i]);
            }
        }
        if(up!=down and left==right){
            for(int j=up;j<=down;j++){
                result.push_back(array[j][left]);
            }
        }
        if(up==down and left==right){
            result.push_back(array[up][left]);
        }
        return result; 
    }
};

时间复杂度:O(mn),其中 m 和 n 分别是输入二维数组的行数和列数。二维数组中的每个元素都要被访问一次。空间复杂度:O(1)。除了输出数组以外,空间复杂度是常数。

笔者小记:

1、如下代码的解读:

static constexpr int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

声明了static关键字和constexpr关键字,其中1、static关键字表示该变量的生命周期是整个程序的执行周期,但其作用域限定在当前文件或当前函数内部,不依赖于类的实例;局部变量在文件范围内有效,防止外部文件访问;局部静态变量在函数调用直接保留其值。2、constexpr关键字表示该变量在编译时就已经计算出并且是常量,不能在程序运行时修改,即directions的值是固定的,且在编译时已经知道。3、int directions[4][2]表示定义的4行2列的数组,与vector<int> directions(动态且灵活的数组)定义的数组不同的是,int directions[4][2]在编译时的大小就已经确定了,不能在运行时修改,int directions[4][2]传统数组中元素的初始化值默认也是0,这与vector<int>动态容器数组初始化默认的值是保持一致的。

2、如下代码的解读:

vector<vector<bool>> visited(rows, vector<bool>(columns))

定义一个二维的vector容器动态数组, vector<vector<bool>> visited;其中visited(row, vector<bool>(columns))被初始化为包含rows行,每行包含columns个布尔值的二维数组,并且所有元素初始化为false值。类似地应用,在图算法中,visited数组常用来标记那些节点已经访问过。

3、如下代码的解读:

vector<int> order(total);

order(total)为容器vector<int>初始化,其中total是一个变量,表示初始化一个大小为total的vector<int>容器,vector容器初始化所有元素都会被默认为0,这与上面初始化vector二维bool数组中的元素为false保持了一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值