一.数组(21)54. 螺旋矩阵

54. 螺旋矩阵

给你一个 mn 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

 

 

方法一:模拟
可以模拟螺旋矩阵的路径。初始位置是矩阵的左上角,初始方向是向右,当路径超出界限或者进入之前访问过的位置时,顺时针旋转,进入下一个方向。

判断路径是否进入之前访问过的位置需要使用一个与输入矩阵大小相同的辅助矩阵 visited,其中的每个元素表示该位置是否被访问过。当一个元素被访问时,将visited 中的对应位置的元素设为已访问。

如何判断路径是否结束?由于矩阵中的每个元素都被访问一次,因此路径的长度即为矩阵中的元素数量,当路径的长度达到矩阵中的元素数量时即为完整路径,将该路径返回。

模拟算法:

解决方法就是根据题目给出的规则对题目要求的相关过程进行编程模拟。在解题时,需要仔细分析题目给出的规则,要尽可能地做到全面地考虑所有可能出现的情况。

许多真实场景下,由于问题规模过大,变量过多等因素,很难将具体的问题抽象出来,也就无法针对抽象问题的特征来进行算法的设计。这个时候,模拟思想或许是最佳的解题策略。

模拟的过程就是对真实场景尽可能的模拟,然后通过计算机强大的计算能力对结果进行预测。是自定义的,任意的输入,不规则的系统响应,但是只为了获得一个可靠的理想的输出。

所谓的模拟题,运用的“模拟算法”,其实并没有什么完全准确的定义。模拟算法,用一句老话说,就是“照着葫芦画瓢”;官方化的诠释则是:根据题目表述进行筛选提取关键要素,按需求书写代码解决实际问题。

模拟算法一般都是一些很基础的题目,一些神犇眼中,模拟题就是所谓的“水题”,不太需要动脑子,只要按照题目要求来就好。

 


 

func spiralOrder(matrix [][]int) []int {
    //len(matrix) == 0 数组为空时返回空(二维数组为空)
    //len(matrix[0]) == 0即n为0,每行都为空,返回空(二维数组的每一维都为空)
    if len(matrix) == 0 || len(matrix[0]) == 0 {
        return []int{}
    }

    rows, columns := len(matrix), len(matrix[0])//mXn
    visited := make([][]bool, rows)//构造二维布尔数组,表示matrix相应位置的元素是否遍历过
    for i := 0; i < rows; i++{
        visited[i] = make([]bool, columns)//构造二维数组的每一维,默认值为false
    }

    var(
        total = rows * columns //矩阵的元素总数
        order = make([]int, total)//用于返回的数组,一维数组来存放要返回的元素
        row, column = 0, 0//初始从左顶点开始
        //代表向 右、下、左、上 移动,这个遍历方向的顺序是按照螺旋顺序来的。每一维第一个元素为行,第二个为列
        directions = [][]int{[]int{0, 1}, []int{1, 0}, []int{0, -1}, []int{-1, 0}}
        directionIndex = 0//初始向右移动
    )

    for i := 0; i < total; i++{//向order中存放矩阵元素
        order[i] = matrix[row][column]
        visited[row][column] = true//遍历过的位置为true

        //下一个要遍历的位置的坐标
        nextRow,  nextColumn := row + directions[directionIndex][0], column + directions[directionIndex][1]
        
        //nextRow < 0 || nextRow >= rows || nextColumn < 0||nextColumn >= columns表示数组越界
        //visited[nextRow][nextColumn]表示该位置已经遍历过
        if nextRow < 0 || nextRow >= rows || nextColumn < 0||nextColumn >= columns || visited[nextRow][nextColumn]{
            directionIndex = (directionIndex + 1) % 4//下一位置为非法位置时,改变方向
        }

        //要遍历的下一个位置的坐标
        row += directions[directionIndex][0]
        column += directions[directionIndex][1]
    }
    return order//返回按照螺旋顺序存放的数组
}

方法二:按层模拟
可以将矩阵看成若干层,首先输出最外层的元素,其次输出次外层的元素,直到输出最内层的元素。

定义矩阵的第 k 层是到最近边界距离为 k 的所有顶点。例如,下图矩阵最外层元素都是第 1 层,次外层元素都是第 2 层,剩下的元素都是第 3 层。


[[1, 1, 1, 1, 1, 1, 1],
 [1, 2, 2, 2, 2, 2, 1],
 [1, 2, 3, 3, 3, 2, 1],
 [1, 2, 2, 2, 2, 2, 1],
 [1, 1, 1, 1, 1, 1, 1]]


对于每层,从左上方开始以顺时针的顺序遍历所有元素。假设当前层的左上角位于(top,left),右下角位于(bottom,right),按照如下顺序遍历当前层的元素。

从左到右遍历上侧元素,依次为(top,left) 到 (top,right)。

从上到下遍历右侧元素,依次为 (top+1,right) 到 (bottom,right)。

如果left<right 且 top<bottom,则从右到左遍历下侧元素,依次为 (bottom,right−1) 到 (bottom,left+1),以及从下到上遍历左侧元素,依次为(bottom,left) 到(top+1,left)。

遍历完当前层的元素之后,将 left 和 top 分别增加 1,将 right 和 bottom 分别减少 1,进入下一层继续遍历,直到遍历完所有元素为止。

func spiralOrder(matrix [][]int) []int {

    if len(matrix) == 0 || len(matrix[0]) == 0 {//二维数组为空,或二维数组的每一维为空
        return []int{}
    }

    var (
        rows, columns = len(matrix), len(matrix[0])//行数列数
        order = make([]int, rows * columns)//按螺旋顺序存放的元素
        index = 0//order数组当前要存放的元素的位置
        left, right, top, bottom = 0, columns - 1, 0, rows - 1//初始最左、最右、最上、最下位置坐标
    )


    for left <= right && top <= bottom {//边界条件,左上点不能在右下点的右下
        for column := left; column <= right; column++ {//向右遍历,行不变 列++
            //存放该层的上边界
            order[index] = matrix[top][column]
            index++
        }
        for row := top + 1; row <= bottom; row++ {//向下遍历
            //存放该层的右边界
            order[index] = matrix[row][right]
            index++
        }
        if left < right && top < bottom {
            for column := right - 1; column > left; column-- {//向左遍历
                //存放该层的下边界
                order[index] = matrix[bottom][column]
                index++
            }
            for row := bottom; row > top; row-- {//向左遍历
                //存放该层的左边界
                order[index] = matrix[row][left]
                index++
            }
        }


        //左上角与右下角坐标更新,变到内层(1层->2层...)
        left++
        right--
        top++
        bottom--
    }
    return order//返回螺旋顺序数组
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值