首页 > 其他分享 >LeetCode59 螺旋矩阵2

LeetCode59 螺旋矩阵2

时间:2024-12-24 23:55:45浏览次数:5  
标签:count 填充 螺旋 元素 矩阵 LeetCode59 循环 offset

原理

本题要求生成一个给定大小 n 的螺旋矩阵,矩阵中的元素按照螺旋顺序从 1 开始依次递增填充。整体思路是通过模拟螺旋的填充路径,一圈一圈地向矩阵内部填充数字,每一圈的填充过程都按照顺时针方向,依次填充上侧行、右侧列、下侧行和左侧列,直到整个矩阵被填满。对于奇数边长的矩阵,最后还需要单独处理正中间的那个元素。

步骤

  1. 初始化相关变量
    • 创建一个 n * n 大小的二维数组 res,并将所有元素初始化为 0,用于存储最终生成的螺旋矩阵。
    • 定义 startx 和 starty,它们表示每循环填充一圈时的起始坐标位置,初始都设为 0,即从矩阵的左上角开始填充第一圈。
    • 计算 loop,它代表总共需要循环填充完整几圈,其值为 n / 2,比如 n 为 4 时,需要完整循环 2 圈;n 为 5 时,完整循环 2 圈后还剩下中间一个元素单独处理。
    • 确定 mid,它表示矩阵中间的位置坐标,用于后续处理奇数边长矩阵中间元素的赋值,计算方式为 n / 2
    • 设定 count 初始值为 1,用于按顺序给矩阵中的元素赋值,每填充一个位置,count 的值就递增 1。
    • 初始化 offset 为 1,这个变量用于控制每一圈中每条边遍历填充的长度,随着圈数的增加,每条边需要填充的长度会逐渐减少,每完成一圈,offset 的值就增加 1。
  2. 循环填充矩阵(外层 while 循环部分)
    • 进入 while 循环,只要 loop 的值大于 0,就代表还需要继续填充完整的圈数。在每次循环中:
      • 先将当前圈的起始填充坐标 i 和 j 分别赋值为 startx 和 starty
      • 填充上侧行(从左到右):通过 for (j; j < n - offset; j++) 循环,从当前起始列 starty 开始,向右填充元素,直到达到本圈右侧边界(n - offset,每一圈右边界会收缩一位,由 offset 控制),每次填充一个元素就将 count 的值赋给当前位置 res[i][j],然后 count 自增 1,模拟按螺旋顺序填充数字的过程。
      • 填充右侧列(从上到下):接着通过 for (i; i < n - offset; i++) 循环,从当前行(此时 i 已经在刚才填充上侧行结束时所在行)开始,向下填充元素,直到达到本圈下侧边界,同样每次填充元素时更新 res[i][j] 和 count 的值。
      • 填充下侧行(从右到左):再通过 for (; j > starty; j--) 循环,从本圈最右侧列(刚才填充右侧列结束时所在列)开始,向左填充元素,直到回到本圈起始列位置(starty),过程中持续更新对应矩阵位置的值和 count
      • 填充左侧列(从下到上):最后通过 for (; i > startx; i--) 循环,从本圈最下侧行(填充下侧行结束时所在行)开始,向上填充元素,直到回到本圈起始行位置(startx),同样更新矩阵元素值和 count
      • 更新下一圈的起始位置和 offset:完成一圈填充后,将 startx 和 starty 的值都加 1,使下一圈的起始位置往矩阵内部移动一位;同时将 offset 的值加 1,使得下一圈每条边遍历填充的长度再收缩一位,以符合螺旋向内填充的规律。
  3. 处理奇数边长矩阵中间元素(if 判断部分)
    如果 n 为奇数,意味着在完成所有完整圈的填充后,矩阵正中间还有一个元素未赋值,此时通过 res[mid][mid] = count; 将 count 的当前值赋给矩阵中间位置的元素,完成整个螺旋矩阵的填充。

图示法表示步骤(以 n = 5 为例)

  1. 初始化矩阵和变量
初始矩阵(5 * 5,全为0):
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

变量情况:startx = 0, starty = 0, loop = 2, mid = 2, count = 1, offset = 1
  1. 第一圈填充
    • 填充上侧行(从左到右)
1 2 3 4 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

此时 j 从 0 循环到 3(n - offset = 5 - 1 = 4),依次填充元素,count 从 1 递增到 4。

  • 填充右侧列(从上到下)
1 2 3 4 0
0 5 6 7 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

i 从 0 循环到 3,填充对应位置元素,count 从 5 递增到 8。

  • 填充下侧行(从右到左)
1 2 3 4 0
0 5 6 7 0
0 0 8 9 0
0 0 0 0 0
0 0 0 0 0

j 从 4 循环到 1,填充元素,count 从 9 递增到 11。

  • 填充左侧列(从下到上)
1 2 3 4 0
0 5 6 7 0
0 0 8 9 0
0 0 10 11 0
0 0 0 0 0

i 从 4 循环到 1,填充元素,count 从 11 递增到 13。

完成第一圈填充后,startx = 1, starty = 1, offset = 2

  1. 第二圈填充
    • 填充上侧行(从左到右)
1 2 3 4 0
0 5 6 7 0
0 0 13 14 0
0 0 10 11 0
0 0 0 0 0

j 从 1 循环到 2(n - offset = 5 - 2 = 3),填充元素,count 从 13 递增到 14。

  • 填充右侧列(从上到下)
1 2 3 4 0
0 5 6 7 0
0 0 13 14 0
0 0 10 15 0
0 0 0 0 0

i 从 1 循环到 2,填充元素,count 从 14 递增到 15。

  • 填充下侧行(从右到左)
1 2 3 4 0
0 5 6 7 0
0 0 13 14 0
0 0 10 15 0
0 0 0 16 0

j 从 2 循环到 1,填充元素,count 从 15 递增到 16。

  • 填充左侧列(从下到上)
1 2 3 4 0
0 5 6 7 0
0 0 13 14 0
0 0 10 15 0
0 0 0 16 0

i 从 2 循环到 1,填充元素,count 从 16 递增到 17。

完成第二圈填充后,此时 loop 变为 0,循环结束。

  1. 处理中间元素(因为 n = 5 为奇数)
1 2 3 4 0
0 5 6 7 0
0 0 13 14 0
0 0 10 15 0
0 0 0 16 17

通过 res[mid][mid] = count;(即 res[2][2] = 17)完成整个螺旋矩阵的填充。

代码关键行注释

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组,初始化n * n大小的矩阵,所有元素初始为0,用于存储螺旋矩阵结果
        int startx = 0, starty = 0; // 定义每循环一个圈的起始位置坐标,初始为左上角(0, 0)
        int loop = n / 2; // 每个圈循环几次,根据n的值计算完整循环的圈数(n为奇数时最后中间元素单独处理)
        int mid = n / 2; // 矩阵中间的位置坐标,用于后续奇数情况处理中间元素赋值
        int count = 1; // 用来给矩阵中每一个空格赋值,从1开始按顺序递增
        int offset = 1; // 需要控制每一条边遍历的长度,每次循环右边界收缩一位,初始为1
        int i, j;
        while (loop--) {  // 只要还需要循环填充完整的圈数,就继续循环
            i = startx;
            j = starty;

            // 下面开始的四个for就是模拟转了一圈
            // 模拟填充上行从左到右(左闭右开),按螺旋顺序从左到右填充当前圈的上侧行元素
            for (j; j < n - offset; j++) {  
                res[i][j] = count++;  
            }
            // 模拟填充右列从上到下(左闭右开),从上到下填充当前圈的右侧列元素
            for (i; i < n - offset; i++) {  
                res[i][j] = count++;  
            }
            // 模拟填充下行从右到左(左闭右开),从右到左填充当前圈的下侧行元素
            for (; j > starty; j--) {  
                res[i][j] = count++;  
            }
            // 模拟填充左列从下到上(左闭右开),从下到上填充当前圈的左侧列元素
            for (; i > startx; i--) {  
                res[i][j] = count++;  
            }

            // 第二圈开始的时候,起始位置要各自加1,更新下一圈的起始坐标位置,往矩阵内部移动一位
            startx++;  
            starty++;  

            // offset 控制每一圈里每一条边遍历的长度,每完成一圈,每条边遍历长度收缩一位,增加1
            offset += 1;  
        }

        // 如果n为奇数的话,需要单独给矩阵最中间的位置赋值,因为奇数边长矩阵在完成圈填充后中间还有一个元素未处理
        if (n % 2) {  
            res[mid][mid] = count;  
        }
        return res;
    }
};

完整代码程序

#include <iostream>
#include <vector>
using namespace std;

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n, vector<int>(n, 0));
        int startx = 0, starty = 0;
        int loop = n / 2;
        int mid = n / 2;
        int count = 1;
        int offset = 1;
        int i, j;
        while (loop--) {
            i = startx;
            j = starty;

            // 填充上行从左到右
            for (j; j < n - offset; j++) {
                res[i][j] = count++;
            }
            // 填充右列从上到下
            for (i; i < n - offset; i++) {
                res[i][j] = count++;
            }
            // 填充下行从右到左
            for (; j > starty; j--) {
                res[i][j] = count++;
            }
            // 填充左列从下到上
            for (; i > startx; i--) {
                res[i][j] = count++;
            }

            startx++;
            starty++;
            offset += 1;
        }

        if (n % 2) {
            res[mid][mid] = count;
        }
        return res;
    }
};

int main() {
    Solution solution;
    int n = 5;  // 这里可自行修改n的值来生成不同大小的螺旋矩阵
    vector<vector<int>> result = solution.generateMatrix(n);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cout << result[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

时间复杂度分析

  1. 整体循环部分
    主要的循环操作是外层的 while 循环,它控制了填充完整圈数的次数,循环次数为 n / 2(向下取整),即最多执行 n / 2 次。在每次循环中,又包含了四个 for 循环,分别用于填充每一圈的四条边,每个 for 循环最多执行 n 次(在第一圈时,四条边的填充长度都接近 n,后续随着圈数增加会逐渐减少,但按最坏情况考虑与 n 相关),而循环内部的操作(给矩阵元素赋值、变量更新等)时间复杂度都是常数级别 O(1)。所以这部分的时间复杂度大致为 ,因为整体的循环次数与 n 的平方相关。
  2. 处理奇数边长矩阵中间元素部分
    如果 n 为奇数,最后有一个单独的赋值操作给矩阵中间元素赋值,时间复杂度为 O(1),相比于前面的循环填充部分,其时间复杂度可以忽略不计。

综合来看,整个算法的时间复杂度为 ,它与生成的螺旋矩阵的边长 n 的平方成正比,随着 n 的增大,生成螺旋矩阵所需的时间会相应增加。

总结

  1. 算法特点及适用场景
    • 特点
      • 该算法通过模拟螺旋的填充路径,逻辑清晰直观,易于理解和实现,按照一圈一圈向内填充的方式,能够准确地生成满足要求的螺旋矩阵。并且代码结构相对简洁,通过合理控制循环和变量,有效地处理了不同边长(包括奇数和偶数)情况下的矩阵填充问题。
      • 时间复杂度为 ,在处理中等规模的螺旋矩阵生成任务时,具有较好的效率表现,能够在合理的时间内完成矩阵填充。
    • 适用场景
      常用于需要按照特定螺旋顺序生成二维数据结构的场景,比如在图像处理中,可能需要按照螺旋顺序对图像像素进行采样、处理或者赋值;在一些数学建模、算法设计等领域,当涉及到以螺旋形式组织数据或者进行数据遍历等操作时,也可以借助此算法来构建相应的基础数据结构(螺旋矩阵),方便后续的计算和分析。
  2. 注意事项及可能遇到的问题
    • 边界情况处理:代码中对于每条边填充的边界控制(如 for 循环的结束条件)以及圈数的计算、奇数边长矩阵中间元素的处理等边界情况需要特别留意,一旦处理不当,很容易导致生成的矩阵不符合螺旋矩阵的要求,出现元素缺失、重复填充或者顺序错误等问题。例如,如果 offset 的计算或使用有误,可能会使每条边的填充长度出错,进而影响整个矩阵的填充结果。
    • 代码可扩展性和通用性:虽然当前算法能够很好地解决 LeetCode 中给定规则的螺旋矩阵生成问题,但如果要对矩阵的填充规则进行修改,比如改变螺旋的方向(变为逆时针)、按照不同的递增规律填充元素(不是简单的连续整数递增)等,可能需要对代码的循环结构和逻辑进行较大幅度的调整,代码的可扩展性和通用性相对有限,在实际应用中如果有多样化的需求变动,可能需要重新设计部分代码逻辑来满足新的要求。

标签:count,填充,螺旋,元素,矩阵,LeetCode59,循环,offset
From: https://blog.csdn.net/m0_49844997/article/details/144705234

相关文章

  • 【无标题】51系列单片机学习:矩阵按键
    文章目录前言一、矩阵按键的硬件连接二、工作原理三、代码编写总结前言矩阵按键是一种通过行列交叉连接的按键阵列,可以节省单片机的IO口资源,用于实现多个按键的输入检测。以下是本文的简要介绍。一、矩阵按键的硬件连接1.将矩阵按键按照图1方式进行连接。图1.矩阵按......
  • 矩阵跨境获客,Tiktok运营低成本实用工具,iOS免越狱中控
    跨境获客,一直是很多商家头疼的问题。今天,小鲸就带大家来认识一个实用工具——Tiktok矩阵实用工具,让你轻松实现高效跨境获客!跨国控制手机苹果云控系统在开始之前,我们先来了解一下什么是跨国控制手机苹果云控系统。简单来说,它是一种可以同时控制多台苹果手机的云端控制系统,用户......
  • 波士顿矩阵与通用电气矩阵:项目管理中的战略工具应用
    在当今竞争激烈的商业环境中,项目管理的成功与否往往决定着企业的兴衰。而在项目管理的众多工具中,波士顿矩阵(BostonMatrix)和通用电气矩阵(GEMatrix)犹如两颗璀璨的明星,为企业提供了极具价值的战略分析视角。这两个矩阵工具能够帮助项目经理和企业决策者清晰地梳理项目组合,合理分配......
  • 9-10矩阵基础
    1.矩阵基础1_tensorflow1版本转2版本代码2.矩阵基础1_打印矩阵维度,提取矩阵数据3.矩阵基础2_矩阵相乘和相加运算4.矩阵基础2_矩阵相乘.multiply方法 ......
  • GESP2级2403 小杨的日字矩阵
    题目描述小杨想要构造一个N×N的日字矩阵(N为奇数),具体来说,这个矩阵共有N行,每行N个字符,其中最左列、最右列都是|,而第一行、最后一行、以及中间一行(即第(N+1)/2 行)的第2~N-1个字符都是-,其余所有字符都是半角小写字母x。例如,一个N=5的日字矩阵如下:|---||xx......
  • 73. 矩阵置零
    题目链接解题思路:如何原地,是困难点。我们可以使用原有的矩阵,来存放某些信息。原来的矩阵第一行,matrix[0][i]如果等于0,代表第i列有0,原来的矩阵第一列,matrix[i][0]如果等于0,代表第i列有0。还有一个注意点,就是matrix[0][0]代表什么?这是一个歧义的点,所以不存放数据,单独用两个变量......
  • python怎么看矩阵维数
    print(X.shape):查看矩阵的行列号print(len(X)):查看矩阵的行数print(X.ndim):查看矩阵的维数1、查看矩阵的行列号2、查看矩阵的行数3、查看矩阵的维数......
  • 54. 螺旋矩阵
    题目链接解题思路:宏观思路,一圈一圈打,确定好「一圈」的左上角以及右下角,然后再打印。有两种特殊情况,左上角和右上角的列相等时,只需要打一行即可;左上角的列和右下角的列相等时,只需打印一列即可。代码:fromtypingimportListclassSolution:defspiralOrder(self,......
  • Unity Shader学习日记 part 3 线性代数--矩阵变换
            之前我们学到了矩阵的相关基础,了解矩阵使用了进行变幻的。可是在三维空间中我们不管是表示点还是向量,都是通过x,y,z来表示的。那我们如何在三维向量中,表示出来变换的呢?齐次坐标    齐次坐标:将原本的n维向量用n+1维来表示。    原因:1.不论是......
  • 2024-12-22:矩阵中的最大得分。用go语言,给定一个由正整数构成的 m x n 矩阵 grid,你可以
    2024-12-22:矩阵中的最大得分。用go语言,给定一个由正整数构成的mxn矩阵grid,你可以从任意单元格开始,移动到正下方或正右侧的任一单元格(不要求相邻)。在从值为c1的单元格移动到值为c2的单元格时,得分计算为c2-c1。你的目标是至少移动一次,并找到能够获得的最大总得......