螺旋矩阵的算法刷题

news2024/9/24 15:16:48

螺旋矩阵的算法刷题

本文主要涉及螺旋矩阵的算法
包括三个题目分别是

  1. 59. 螺旋矩阵 II
  2. 54. 螺旋矩阵 中等
  3. LCR 146. 螺旋遍历二维数组

文章目录

  • 螺旋矩阵的算法刷题
    • 一 、螺旋矩阵简单
      • 1.1 实现一(我认为这个方法更巧妙!!)
      • 1.2 实现二(经典方法--更加直观)
    • 二、螺旋矩阵 中等
      • 2.1 实现一(经典)
      • 2.2 实现二(精妙!!)
    • 三、螺旋遍历矩阵 简单

一 、螺旋矩阵简单

59. 螺旋矩阵 II

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix .

在这里插入图片描述

1.1 实现一(我认为这个方法更巧妙!!)

思路
采用了类似螺旋走位的方式进行填充,具体实现如下:

  1. 首先,定义一个n x n的二维数组res来存储结果。
  2. 初始化方向向量dx和dy,初始值分别为0和1,表示向右移动。
  3. 初始化坐标x和y,初始值都为0,表示从矩阵的左上角开始填充。
  4. 使用一个循环,从1到n * n遍历每个要填充的数字。
  5. 在每一步中,将当前位置的值设置为当前遍历的数字i,并根据当前方向向量(dx, dy)进行移动到下一个位置。
  6. 判断下一个位置是否已经被填充过,如果是,则表示已经到达边界,需要调转方向向量以继续填充;如果不是,则继续沿当前方向向量移动。
  7. 返回填充完成的二维数组res作为结果。

这样的算法可以确保填充的数字形成了一个螺旋状的矩阵。

C++代码实现

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> matrix(n,vector<int>(n,0));
        //代表方向的向量变量 01--右 10--下 -1 0--左 0 -1 --上
        int dx=0,dy=1;
        //x,y表示当前坐标
        int x=0,y=0;
        //使用for进行循环,i表示当前值
        for(int i =1; i<=n*n;i++)
        {
            matrix[x][y]=i;
            //**关键部分**
            if(matrix[(x+dx+n)%n][(y+dy+n)%n]!=0)//判断是否已经填充,如果已经填充则向量需要换方向
            {
                int temp = dy;
                dy=-dx;
                dx=temp;
            }
            x+=dx;
            y+=dy;
        }
        return matrix;    
    }
};

重要的是下面这段代码

 if(matrix[(x+dx+n)%n][(y+dy+n)%n]!=0)//判断是否已经填充,如果已经填充则向量需要换方向
            {
                int temp = dy;
                dy=-dx;
                dx=temp;
            }

这段代码是在判断当前位置的下一个位置是否已经被填充过。如果下一个位置已经被填充过,说明当前位置已经是当前层的最后一个位置,需要改变填充方向。

逐行解释这段代码:

  1. (x + dx + n) % n:这里计算了下一个位置的横坐标。由于是螺旋填充,因此下一个位置可能超出边界。使用 % n 可以将超出边界的情况转化为在边界内的情况。例如,当 x + dx 大于等于 n 时,超出了右边界,通过 % n 可以将其映射到左边界,从而形成循环填充。
  2. 同理,(y + dy + n) % n 计算了下一个位置的纵坐标。
  3. res[(x + dx + n) % n][(y + dy + n) % n] != 0:这个条件判断当前位置的下一个位置是否已经被填充过。如果下一个位置不等于0,说明已经被填充过。
  4. 如果下一个位置已经被填充过,则需要改变填充方向。通过交换 dxdy 实现了向下转向的功能。
  5. int tem = dy; dy = -dx; dx = tem;:这里使用了一个临时变量 tem,先将 dy 的值保存下来,然后将 dy 设置为 -dxdx 设置为 tem,实现了方向的转换。

在这里插入图片描述

总的来说,这段代码的作用是在当前位置的下一个位置已经被填充过时,改变填充方向,从而继续填充螺旋矩阵。

1.2 实现二(经典方法–更加直观)

一个常见的方法是按层次遍历,逐层填充螺旋矩阵。具体步骤如下:

  1. 初始化一个n x n的二维数组res,用于存储结果。
  2. 定义四个变量top、bottom、left、right,分别表示当前填充层的上下左右边界。
  3. 初始化这些边界值:top=0,bottom=n-1,left=0,right=n-1。
  4. 初始化一个变量num,表示当前要填充的数字,初始值为1。
  5. 在一个循环中,依次填充每一层的数字,直到所有位置都被填充为止。
    • 从左到右,将当前层的最上一行从left到right填充为num开始递增的数字,同时更新top的值使其向下移动一行。
    • 从上到下,将当前层的最右一列从top到bottom填充为num开始递增的数字,同时更新right的值使其向左移动一列。
    • 从右到左,将当前层的最下一行从right到left填充为num开始递增的数字,同时更新bottom的值使其向上移动一行。
    • 从下到上,将当前层的最左一列从bottom到top填充为num开始递增的数字,同时更新left的值使其向右移动一列。
    • 每填充完一行或一列后,将num递增。
  6. 当top > bottom 或 left > right时,表示所有位置都被填充完毕,退出循环。
  7. 返回填充完成的二维数组res作为结果。

这样的实现方法更直观清晰,容易理解和实现,并且效率也很高。

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n, vector<int>(n, 0)); // 初始化结果矩阵为全零
        int top = 0, bottom = n - 1, left = 0, right = n - 1; // 初始化边界
        int num = 1; // 当前要填充的数字,初始为1
        
        while (true) {
            // 从左到右,填充最上一行
            for (int i = left; i <= right; ++i) {
                res[top][i] = num++;
            }
            if (++top > bottom) break; // 更新上边界,若超过下边界则退出
            
            // 从上到下,填充最右一列
            for (int i = top; i <= bottom; ++i) {
                res[i][right] = num++;
            }
            if (--right < left) break; // 更新右边界,若超过左边界则退出
            
            // 从右到左,填充最下一行
            for (int i = right; i >= left; --i) {
                res[bottom][i] = num++;
            }
            if (--bottom < top) break; // 更新下边界,若超过上边界则退出
            
            // 从下到上,填充最左一列
            for (int i = bottom; i >= top; --i) {
                res[i][left] = num++;
            }
            if (++left > right) break; // 更新左边界,若超过右边界则退出
        }
        
        return res;
    }
};

二、螺旋矩阵 中等

54. 螺旋矩阵 中等

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

在这里插入图片描述

2.1 实现一(经典)

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int> ans;
        if (matrix.empty()) return ans;

        int m = matrix.size(); // 行数
        int n = matrix[0].size(); // 列数
        int top = 0, bottom = m - 1, left = 0, right = n - 1;

        while (top <= bottom && left <= right) {
            // 从左到右遍历上边界
            for (int j = left; j <= right; ++j) {
                ans.push_back(matrix[top][j]);
            }
            ++top;

            // 从上到下遍历右边界
            for (int i = top; i <= bottom; ++i) {
                ans.push_back(matrix[i][right]);
            }
            --right;

            // 从右到左遍历下边界
            if (top <= bottom) { // 防止重复访问上一次遍历的元素
                for (int j = right; j >= left; --j) {
                    ans.push_back(matrix[bottom][j]);
                }
                --bottom;
            }

            // 从下到上遍历左边界
            if (left <= right) { // 防止重复访问上一次遍历的元素
                for (int i = bottom; i >= top; --i) {
                    ans.push_back(matrix[i][left]);
                }
                ++left;
            }
        }

        return ans;
    }
};

按照螺旋顺序遍历二维矩阵。下面是对代码的详细解释:

  1. 首先,定义一个空的整数向量 ans 用于存储遍历结果。
  2. 如果输入的二维矩阵 matrix 为空,直接返回空的结果向量 ans
  3. 获取矩阵的行数 m 和列数 n,以及初始化四个边界变量 topbottomleftright
  4. 在一个循环中,不断遍历矩阵的边界元素,直到边界收缩到无效为止。
    • 从左到右遍历上边界,将上边界的元素依次加入结果向量 ans 中,并将 top 上移一行。
    • 从上到下遍历右边界,将右边界的元素依次加入结果向量 ans 中,并将 right 右移一列。
    • 从右到左遍历下边界,将下边界的元素依次加入结果向量 ans 中,并将 bottom 下移一行(注意要判断 top <= bottom,防止重复遍历)。
    • 从下到上遍历左边界,将左边界的元素依次加入结果向量 ans 中,并将 left 左移一列(注意要判断 left <= right,防止重复遍历)。
  5. 循环结束后,返回结果向量 ans

利用边界变量控制了螺旋顺序的遍历过程,实现了按照螺旋顺序遍历二维矩阵的功能。

2.2 实现二(精妙!!)

下面是一段Python代码:

def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        res = []
        while matrix:
            # 削头(第一层)
            res += matrix.pop(0)
            # 将剩下的逆时针转九十度,等待下次被削
            matrix = list(zip(*matrix))[::-1]
        return res

采用了不断“削头”的方式来实现。具体的步骤如下:

  1. 创建一个空列表 res 用于存储螺旋顺序遍历的结果。
  2. 使用 while matrix: 循环来遍历矩阵,直到矩阵为空。
  3. 在每一次循环中,先将矩阵的第一行(即第一层)添加到结果列表 res 中,然后将该行从矩阵中移除。
  4. 接着,将剩下的矩阵逆时针旋转 90 度(将其转置后再沿着竖直方向反转),以便下次循环时能够继续削头。
  5. 重复上述步骤直到矩阵为空。

这种方法的思路简洁,通过不断调整矩阵的结构来实现螺旋顺序遍历,避免了显式的控制遍历方向和边界条件的处理。

在实际代码中,matrix.pop(0) 用于削头,list(zip(*matrix))[::-1] 用于逆时针旋转矩阵。

如果是C++实现类似削头的效果:

#include <vector>

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int> res;
        while (!matrix.empty()) {
            // 削头(第一层)
            for (auto it = matrix[0].begin(); it != matrix[0].end(); ++it) {
                res.push_back(*it);
            }
            matrix.erase(matrix.begin()); // 删除第一行

            // 将剩下的逆时针转九十度,等待下次被削
            if (!matrix.empty()) {
                vector<vector<int>> new_matrix;
                for (int j = matrix[0].size() - 1; j >= 0; --j) {
                    vector<int> column;
                    for (int i = 0; i < matrix.size(); ++i) {
                        column.push_back(matrix[i][j]);
                    }
                    new_matrix.push_back(column);
                }
                matrix = new_matrix;
            }
        }
        return res;
    }
};

三、螺旋遍历矩阵 简单

LCR 146. 螺旋遍历二维数组

上面这个题是企业题,同类型的题目可以多练习练习~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1551965.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

谷歌seo外推是什么?

​针对谷歌&#xff0c;站外推广是个不可忽视的环节&#xff0c;外推&#xff0c;也就是站外推广&#xff0c;就是所有在你的网站之外发生的活动&#xff0c;都是为了提升你的品牌在谷歌搜索结果中的排名&#xff0c;但其实本质依旧是外链&#xff0c;也就是指向你网站的链接&a…

【JavaScript】数组 ② ( JavaScript 数组索引 | JavaScript 遍历数组 | 使用 for 循环遍历数组 )

文章目录 一、JavaScript 数组索引1、数组索引2、数组索引 - 代码示例 二、JavaScript 遍历数组1、使用 for 循环遍历数组2、使用 for 循环遍历数组 - 代码示例 一、JavaScript 数组索引 1、数组索引 在 JavaScript 中 , 数组 的 " 索引 " 又称为 " 下标 "…

NVIDIA H200 创下 MLPerf LLM 最新推理记录

NVIDIA H200 Tensor Core GPU 和 NVIDIA TensorRT-LLM 创下 MLPerf LLM 最新推理记录 生成式人工智能正在解锁新的计算应用程序&#xff0c;通过持续的模型创新来极大地增强人类的能力。 生成式 AI 模型&#xff08;包括大型语言模型 (LLM)&#xff09;用于制作营销文案、编写计…

NOIP,CSP-J,CSP-S——树

一、树 概念: 节点、深度、路径、边 树的直径 真题: 答案:B 答案:A 一个树的边是n-1 现在是m,所以m-(n-1)=m-n+1

C++基础11:模板与命名空间

此专栏为移动机器人知识体系下的编程语言中的 C {\rm C} C从入门到深入的专栏&#xff0c;参考书籍&#xff1a;《深入浅出 C {\rm C} C》(马晓锐)和《从 C {\rm C} C到 C {\rm C} C精通面向对象编程》(曾凡锋等)。 10.模板与命名空间 10.1 模板简述 模板使函数和类的处理对象…

Hbase 王者荣耀数据表 HBase常用Shell命令

大数据课本&#xff1a; HBase常用Shell命令 在使用具体的Shell命令操作HBase数据之前&#xff0c;需要首先启动Hadoop&#xff0c;然后再启动HBase&#xff0c;并且启动HBase Shell&#xff0c;进入Shell命令提示符状态&#xff0c;具体命令如下&#xff1a; $ cd /usr/local…

13.软件测试过程与管理

重点&#xff1a; 软件测试的成本管理&#xff1a;区分一致性成本和非一致性成本&#xff1b;根据题干描述计算质量成本、投资回报率&#xff1b; DDP&#xff1a;缺陷探测率的计算&#xff1b;下午题考的频率高&#xff1b; 主要议题&#xff1a; 1.软件测试过程 2.软件测试…

教育建筑智慧能源管理平台解决方案【新型电力系统下的绿色校园能源管理平台】

一、行业特点 1.建筑类型多&#xff1a;集教学、科研、生活于一体&#xff0c;占地面积大&#xff0c;建筑类型多&#xff0c;功能划分复杂。 2.供电可靠性要求高&#xff1a;教育建筑中的高层建筑、图书馆、实验楼等特级和一级负荷比较多&#xff0c;一旦发生故障会危及生命…

【IP 组播】PIM-SM

目录 原理概述 实验目的 实验内容 实验拓扑 1.基本配置 2.配置IGP 3.配置PIM-SM 4.用户端DR与组播源端DR 5.从RPT切换到SPT 6.配置PIM-Silent接口 原理概述 PIM-SM 是一种基于Group-Shared Tree 的组播路由协议&#xff0c;与 PIM-DM 不同&#xff0c;它适合于组播组成…

C++模板类和模板函数

模板类 #include<bits/stdc.h> using namespace std; template<typename T> class People{public:People(T name):name_(name){}protected:T name_; }; class A:public People<string>{public:A(string name): People(name){}void print(){std::cout<<…

启动性能优化

一、应用启动慢的原因 1.在主线程执行了太多耗时的操作&#xff0c;比如加载数据&#xff0c;或者初始化三方库等等&#xff0c;导致在Application的oncreate或者Activity的oncreate方法中耗时太久 2.布局嵌套太深&#xff0c;或者一些不会立即使用的布局也在一开始一起加载到…

tomcat配置静态资源后无法正常访问

目录 一、场景二、配置三、访问异常四、排查五、原因六、解决 一、场景 1、将前端文件存在到指定目录 2、在tomcat配置静态资源 3、配置后无法正常访问到前端文件 二、配置 1、tomcat配置 2、静态资源 三、访问异常 四、排查 可以ping通&#xff0c;但是访问不了3080端口 …

4G CAT.1单灯控制器 计讯物联TL112

传统的照明控制方式往往需要通过开关进行操作&#xff0c;而单灯控制器的出现改变了这一局面。计讯物联单灯控制器TL112&#xff0c;一种基于无线通信技术的智能照明控制设备&#xff0c;可通过管理中心平台、pc电脑端、手机端等多方式&#xff0c;实现对路灯的智能控制&#x…

Springboot整合瀚高

需要下载highgo驱动,然后将jar包打入进自己本地maven中 下载地址: highgi6.2.4 1.打开jar包所在的文件&#xff0c;然后在该文件夹中打开命令窗口&#xff08;或者先打开命令窗口&#xff0c;然后cd到jar所在文件夹&#xff09; install-file -Dfile&#xff1a;jar包名Dart…

SQL Server事务复制操作出现的错误 进程无法在“xxx”上执行sp_replcmds

SQL Server事务复制操作出现的错误 进程无法在“xxx”上执行“sp_replcmds” 无法作为数据库主体执行&#xff0c;因为主体 "dbo" 不存在、无法模拟这种类型的主体&#xff0c;或您没有所需的权限

机器学习——元学习

元学习&#xff08;Meta Learning&#xff09;是一种机器学习方法&#xff0c;旨在使模型能够学习如何学习。它涉及到在学习过程中自动化地学习和优化学习算法或模型的能力。元学习的目标是使模型能够从有限的训练样本中快速适应新任务或新环境。 在传统的机器学习中&#xff…

Linux 反引号、单引号以及双引号的区别

1.单引号—— 单引号中所有的字符包括特殊字符&#xff08;$,,和\&#xff09;都将解释成字符本身而成为普通字符。它不会解析任何变量&#xff0c;元字符&#xff0c;通配符&#xff0c;转义符&#xff0c;只被当作字符串处理。 2.双引号——" 双引号&#xff0c;除了$,…

遥感卫星影像质量评价指标汇总

1. 主观评价方法 以人为图像的评价者&#xff0c;根据自己的评价尺度和经验对图像质量进行评价。 2. 客观评价方法 1)均方差 2)信噪比 主要用来评价影像经压缩、传输、增强等处理前后的质量变化情况&#xff0c;其本质与均方差类似。 3)方差 反映了图像各个像元灰度相对…

游戏本笔记本更换@添加内存条实操示例@DDR5内存条

文章目录 添加内存条的意义准备工具设备拔出电源适配器并关机&#x1f47a;样机 内存条上的金手指安装过程Notes 安装后开机初次开机速度屏幕显示分辨率和闪烁问题检查安装后的效果 添加内存条的意义 参考双通道内存DDR5多通道内存-CSDN博客 准备工具 准备一个质量差不多的螺…