力扣刷题-数组-螺旋矩阵

文章讲述了如何通过编程模拟顺时针画螺旋矩阵,关键在于理解和应用循环不变量原则,即保证上行、右列、下行和左列的遍历规则,并遵循左闭右开原则。针对不同形状的矩阵(如正方形和长方形),处理边界条件是解决问题的关键。
摘要由CSDN通过智能技术生成

模拟过程,但却十分考察对代码的掌控能力。
重点:循环不变量原则!
第一条原则:
模拟顺时针画矩阵的过程:

  • 填充上行从左到右
  • 填充右列从上到下
  • 填充下行从右到左
  • 填充左列从下到上

由外向内一圈一圈这么画下去。
第二条原则:
左闭右开原则!
这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。
image.png
这里每一种颜色,代表一条边,我们遍历的长度,可以看出每一个拐角处的处理规则,拐角处让给新的一条边来继续画。这也是坚持了每条边左闭右开的原则。

59.螺旋矩阵II

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
image.png
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]

class Solution(object):
    def generateMatrix(self, n):
        """
        :type n: int
        :rtype: List[List[int]]
        """
        # 螺旋矩阵 重点考察代码能力 不涉及算法
        # 遇到判断循环边界的题目 要遵循 循环不变量 1. 上下行 左右列的遍历原则 2. 左闭右开原则
        result = [[0]*n for _ in range(n)]
        startx ,starty = 0, 0 # 起始位置 因为一般是循环里面用i j 
        loop = n // 2 # 循环个数 需要转几个圈
        mid = n // 2 # 若n为奇数 需要单独处理中间的一个数 result[mid][mid]
        offset = 1 # 第一圈offset当然为1 下一圈需要再加1 offset目的就是保持左闭右开原则
        count = 1 # 计数 就是每个位置填充的数

        # 开始循环
        while loop > 0:
            i = startx # 对于不同循环 每次起始的x y 不一样
            j = starty
            # 上行 左闭右开
            for j in range(starty, n-offset):
                result[startx][j] = count
                count += 1

            j += 1
            # 右列
            for i in range(startx, n-offset):
                result[i][j] = count
                count += 1
            i += 1
            # 下行
            while j > starty:
                result[i][j] = count
                count += 1
                j -= 1
            # 左列
            while i > startx:
                result[i][j] = count
                count += 1
                i -= 1
            
            loop -= 1 # 循环数减1
            offset += 1 # offset加1

            startx += 1
            starty += 1
        
        if n %2 :
            result[mid][mid] = count

        return result

54.螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
示例 1:(方阵)
image.png
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:
image.png
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
解题:
与59.螺旋矩阵II相同的是:两者都是模拟矩形的顺时针旋转,所以核心依然是依然是坚持循环不变量,按照左闭右开的原则
与59.螺旋矩阵II不同的是:前题中的螺旋矩阵是正方形,只有正方形的边长n一个边界条件,而本题中,需要考虑长方形的长和宽(m行和n列)两个边界条件。自然,m可以等于n,即前题可视为本题在m==n的特殊情况。
我们从最一般的情况开始考虑,与59.螺旋矩阵II题解对比起来,m和n的带入,主要引来两方面的差异:

  • **loop的计算: **本题的loop计算与59.螺旋矩阵II算法略微差异,因为存在rows和columns两个维度,可自行分析,loop只能取min(rows, columns),例如rows = 5, columns = 7,那loop = 5 / 7 = 2
  • mid的计算及填充: 1、同样的原理,本题的mid计算也存在上述差异; 2、 如果min(rows, columns)为偶数,则不需要在最后单独考虑矩阵最中间位置的赋值 如果min(rows, columns)为奇数,则矩阵最中间位置不只是[mid][mid],而是会留下来一个特殊的中间行或者中间列,具体是中间行还是中间列,要看rows和columns的大小,如果rows > columns,则是中间列,相反,则是中间行

可以自己举例画图分析。

class Solution(object):
    def spiralOrder(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        m = len(matrix) # 行数
        n = len(matrix[0]) # 列数
        result = []
        if m == n: # 当矩阵为方阵时
            startx, starty = 0, 0
            offset = 1
            loop = n // 2
            mid = n // 2
            while loop:
                i = startx
                j = starty
                # 上行
                for j in range(starty, n-offset):
                    result.append(matrix[startx][j])
                j += 1
                # 右列
                for i in range(startx, n-offset):
                    result.append(matrix[i][j])
                i += 1
                # 下行
                while j > starty:
                    result.append(matrix[i][j])
                    j -= 1
                # 左列
                while i > startx:
                    result.append(matrix[i][j])
                
                loop -= 1
                offset += 1

                startx += 1
                starty += 1
            
            if n % 2:
                result.append(matrix[mid][mid])
        # m与n不等 则loop循环数量为 min(m,n) // 2 并且如果min(m,n)为奇数 则不再是中间位置 而可能是留下一行或者一列
        if m != n:
            startx, starty = 0, 0
            offset = 1
            loop = min(m ,n ) // 2
            mid = min(m, n) // 2
            while loop:
                i = startx
                j = starty
                for j in range(starty, starty+n-offset): # 注意是startx+n-offset
                    result.append(matrix[startx][j])
                j += 1
                for i in range(startx, startx+m-offset):
                    result.append(matrix[i][j])
                i += 1
                while j > starty:
                    result.append(matrix[i][j])
                    j -= 1
                while i > startx:
                    result.append(matrix[i][j])
                    i -= 1
                
                loop -= 1
                offset += 2 # 注意这里要加2 因为外圈遍历完后,再遍历内圈,内圈的矩阵行和列相比外圈都少了两个长度

                startx += 1
                starty += 1
            # 处理额外的行/列数据
            if min(m,n)%2:
                if m>n: # 那就是额外的列
                    for i in range(mid, mid+m-n+1):
                        result.append(matrix[i][mid])
                else:
                    for j in range(mid, mid+n-m+1):
                        result.append(matrix[mid][j])
        return result

其实可以不用分:

class Solution(object):
    def spiralOrder(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        m = len(matrix) # 行数
        n = len(matrix[0]) # 列数
        result = []
        # if m == n: # 当矩阵为方阵时
        #     startx, starty = 0, 0
        #     offset = 1
        #     loop = n // 2
        #     mid = n // 2
        #     while loop:
        #         i = startx
        #         j = starty
        #         # 上行
        #         for j in range(starty, n-offset):
        #             result.append(matrix[startx][j])
        #         j += 1
        #         # 右列
        #         for i in range(startx, n-offset):
        #             result.append(matrix[i][j])
        #         i += 1
        #         # 下行
        #         while j > starty:
        #             result.append(matrix[i][j])
        #             j -= 1
        #         # 左列
        #         while i > startx:
        #             result.append(matrix[i][j])
                
        #         loop -= 1
        #         offset += 1

        #         startx += 1
        #         starty += 1
            
        #     if n % 2:
        #         result.append(matrix[mid][mid])
        # m与n不等 则loop循环数量为 min(m,n) // 2 并且如果min(m,n)为奇数 则不再是中间位置 而可能是留下一行或者一列
        startx, starty = 0, 0
        offset = 1
        loop = min(m ,n ) // 2
        mid = min(m, n) // 2
        while loop:
            i = startx
            j = starty
            for j in range(starty, starty+n-offset): # 注意是startx+n-offset
                result.append(matrix[startx][j])
            j += 1
            for i in range(startx, startx+m-offset):
                result.append(matrix[i][j])
            i += 1
            while j > starty:
                result.append(matrix[i][j])
                j -= 1
            while i > startx:
                result.append(matrix[i][j])
                i-=1
                
            loop -= 1
            offset += 2 # 注意这里要加2 因为外圈遍历完后,再遍历内圈,内圈的矩阵行和列相比外圈都少了两个长度

            startx += 1
            starty += 1
        # 处理额外的行/列数据
        if min(m,n)%2:
            if m>n: # 那就是额外的列
                for i in range(mid, mid+m-n+1):  # 注意是mid
                    result.append(matrix[i][mid])
            else: # 那就是额外的行
                for j in range(mid, mid+n-m+1):
                    result.append(matrix[mid][j])
        return result

重点在于考虑到m!=n时候如何判断循环圈数,以及什么时候要考虑循环完毕之后剩余的元素,以及如何处理。

参考:https://programmercarl.com/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值