Leetcode 螺旋矩阵

news2024/11/16 18:03:35

在这里插入图片描述

算法思想:

这个算法的目标是按照顺时针螺旋的顺序从矩阵中取出元素。为了做到这一点,整个思路可以分成几个关键步骤:

  1. 定义边界:首先需要定义四个边界变量:

    • left:当前左边界的索引。
    • right:当前右边界的索引。
    • top:当前上边界的索引。
    • bottom:当前下边界的索引。

    这些变量一开始会对应矩阵的外边界,然后我们会通过逐步缩小这些边界来遍历矩阵的每一层。

  2. 螺旋遍历:接下来我们会按照四个方向进行遍历:

    • 从左到右:遍历上边界的所有元素,从左到右遍历,遍历结束后将上边界向下移动一行(top++)。
    • 从上到下:遍历右边界的所有元素,从上到下遍历,遍历结束后将右边界向左移动一列(right--)。
    • 从右到左:如果还有剩余行需要遍历(即top <= bottom),遍历下边界的所有元素,从右到左遍历,遍历结束后将下边界向上移动一行(bottom--)。
    • 从下到上:如果还有剩余列需要遍历(即left <= right),遍历左边界的所有元素,从下到上遍历,遍历结束后将左边界向右移动一列(left++)。
  3. 边界条件检查:在每次从右到左或从下到上遍历之前,需要检查是否仍然有需要遍历的行或列。这是因为在某些情况下,矩阵的某些边界可能已经被其他方向的遍历覆盖了。

  4. 终止条件:当left > right或者top > bottom时,意味着已经没有元素可以遍历了,这时循环终止。

算法流程:

  • 我们从矩阵的左上角开始,按照顺时针的顺序依次遍历。
  • 每完成一圈的遍历,我们就收缩边界,继续处理内层的矩阵,直到所有元素都被处理为止。

时间复杂度分析:

  • 每次遍历矩阵中的一圈元素,而每个元素最多被访问一次,因此时间复杂度是 O(m * n),其中 m 是矩阵的行数,n 是列数。
class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        //首先创建一个存储结果的ArrayList
        List<Integer> result = new ArrayList <>();
        
        //获得矩阵的行和列
        int rows = matrix.length;
        int cols = matrix[0].length;

        //然后定义四周墙壁
        int left = 0;
        int right = cols - 1;
        int top = 0;
        int bottom = rows - 1;

        //开始遍历
        //最后一个元素是矩阵正中心时(left = result && top = bottom),所以循环终止条件是小于等于
        while(left <= right && top <= bottom) { 
            //先从左往右遍历上方的每一行, i 从 left 开始遍历到 right, 而不是从 0 开始
            for(int i = left; i <= right; i++) {
                result.add(matrix[top][i]);
            }
            //每遍历完上方的一行,需要更新 top
            top++;

            //然后从上往下遍历最右边一列
            for(int i = top; i <= bottom; i++) {
                result.add(matrix[i][right]);
            }
            //每次遍历完最右边列,需要更新right
            right--;

            if (top <= bottom) {//检查是否还有需要遍历的行
                //然后从右往左遍历最下方的行
                for(int i = right; i >= left; i--) {
                    result.add(matrix[bottom][i]);
                }
                //每次遍历完最下行,需要更新bottom
                bottom--;
            }

            if (left <= right) {//检查是否还有需要遍历的列
                //然后下往上遍历最左方的列
                for(int i = bottom; i >= top; i--) {
                    result.add(matrix[i][left]);
                }
                //每次遍历完最左方的列,需要更新left
                left++;
            }
        }
        return result;
    }
}

为什么这一部分代码片段,需要使用2个 if 进行判断?

            if (top <= bottom) {
                // Traverse from right to left
                for (int i = right; i >= left; i--) {
                    result.add(matrix[bottom][i]);
                }
                bottom--;
            }

            if (left <= right) {
                // Traverse from bottom to top
                for (int i = bottom; i >= top; i--) {
                    result.add(matrix[i][left]);
                }
                left++;
            }

在这段代码中,两个 if 判断是用来确保在进行从右到左、从下到上的遍历时,矩阵的边界没有发生交叉。如果不进行这些判断,可能会出现重复遍历或错误遍历的情况。

让我们具体分析这两个 if 判断的作用:

1. 第一个 if 判断 (if (top <= bottom))

if (top <= bottom) {
    // Traverse from right to left
    for (int i = right; i >= left; i--) {
        result.add(matrix[bottom][i]);
    }
    bottom--;
}
  • 这个 if 判断用于检查当前上边界 top 是否仍然小于等于下边界 bottom,也就是说,检查是否还有需要遍历的行。如果 top > bottom,说明矩阵的所有行已经遍历完了,再进行从右到左的操作将是没有意义的,甚至可能导致重复遍历或越界。

  • 如果 top <= bottom,则表示当前还有未处理的行,于是可以安全地从右边界 right 开始向左遍历,并在遍历结束后将下边界 bottom 向上移动,减少一行。

2. 第二个 if 判断 (if (left <= right))

if (left <= right) {
    // Traverse from bottom to top
    for (int i = bottom; i >= top; i--) {
        result.add(matrix[i][left]);
    }
    left++;
}
  • 这个 if 判断用于检查当前左边界 left 是否仍然小于等于右边界 right,也就是说,检查是否还有需要遍历的列。如果 left > right,说明矩阵的所有列已经遍历完了,再进行从下到上的操作将是没有必要的,可能导致重复操作或者越界。

  • 如果 left <= right,则表示当前还有未处理的列,可以从下边界 bottom 向上遍历左边界的元素,并在遍历结束后将左边界 left 向右移动,减少一列。

为什么需要两个独立的 if 判断?

  1. 防止重复遍历:在螺旋遍历中,每次都会收缩边界(上、下、左、右)。当已经遍历完一层的矩阵后,边界会逐渐靠近矩阵的中心。如果没有这些 if 判断,在某些情况下,比如在最后一层矩阵中,可能会导致重复遍历某些行或列。例如,如果你在遍历完右到左的行后,紧接着遍历从下到上的列,而此时没有检查边界是否已经重叠,可能会再次遍历已经处理过的行或列。

  2. 防止越界:螺旋遍历会不断缩小边界,如果不做这些检查,当边界重叠或交叉时,程序可能会试图访问无效的数组索引,导致数组越界异常。通过在每次遍历前检查边界的有效性,可以确保访问的数组索引始终在合法范围内。

举例:

假设你有一个 3x3 的矩阵:

1  2  3
4  5  6
7  8  9

按顺时针遍历的顺序是:

  • 从左到右遍历第一行 [1, 2, 3]
  • 从上到下遍历第三列 [6, 9]
  • 从右到左遍历第三行 [8, 7]
  • 从下到上遍历第一列 [4]
  • 最后剩下中心元素 [5]

在这过程中,随着边界逐渐收缩,如果没有 if 判断,在遍历完成第三行时,可能会再次从右到左遍历已处理过的行,或者尝试遍历不存在的行。两个 if 判断就是为了避免这种情况发生。

总结:

  • if (top <= bottom)if (left <= right) 的作用是确保遍历时矩阵的边界没有交叉,从而避免重复访问或越界访问。
  • 它们是为了保证代码在任意大小和形状的矩阵上都能正确运行,特别是在螺旋遍历的过程中边界不断收缩的情况下。

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

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

相关文章

uniapp 实现3d轮播图,也就是中间的放大两边的缩小 用swiper和swiper-item就能实现

话不多说&#xff0c;直接上代码&#xff0c;无需引入外部资源&#xff0c; 用swiper和swiper-item就能实现 先上结构代码 <swiper class"header" circular previous-margin"80rpx" next-margin"60rpx" :current"current"change&…

点亮城市安全:高科技助力精准定位路灯漏电‘隐形杀手

在城市的每一个角落&#xff0c;路灯如同守夜人&#xff0c;默默照亮归家的路。然而&#xff0c;当这些守护者出现“漏电”隐患时&#xff0c;不仅威胁着行人的安全&#xff0c;还可能引发一系列电气故障。那么&#xff0c;如何精准快速地找出这些隐藏的漏电点&#xff0c;并有…

二叉树进阶oj题【二叉树相关10道oj题的解析和代码实现】

目录 二叉树进阶oj题1.根据二叉树创建字符串2.二叉树的层序遍历3.二叉树的层序遍历 II4.二叉树的最近公共祖先5.二叉搜索树和双向链表6.从前序与中序遍历序列构造二叉树7.从中序和后序遍历序列来构造二叉树8.二叉树的前序遍历&#xff0c;非递归迭代实现9.二叉树中序遍历 &…

防止电脑电池老化,禁止usb或者ac接口调试时充电

控制android系统&#xff0c;开发者模式&#xff0c;开启和禁止充电 连接 Android 手机到电脑的 USB 端口。 下载并安装 Android Debug Bridge (ADB) 工具[1]。 USB&#xff1a; 在命令行中输入 adb shell dumpsys battery set usb 0&#xff0c;以禁止 USB 充电。 在命令…

【AI创作组】Matlab中进行符号计算

提示:代码一定要自己运行过才算数…… 1. 符号计算工具箱介绍 1.1 工具箱功能 MATLAB的符号计算工具箱,即Symbolic Math Toolbox,是一套强大的数学软件工具,它使得MATLAB具备了符号运算的能力。该工具箱提供了一系列函数,用于求解、绘制和操作符号数学方程。用户可以直接…

[Linux]从零开始的Minecraft服务器搭建教程

一、前言 学习Linux有一段时间了&#xff0c;当然&#xff0c;我们要把学习的知识运用到实际生活中去。最近朋友们都在玩我的世界&#xff0c;网易版的我的世界联机非常不稳定&#xff0c;用起来也算是非常难受了。所以还是准备转战JAVA版。为了联机&#xff0c;可以考虑一个人…

ARM单片机的内存分布(重要)

ARM单片机的内存分布&#xff08;重要&#xff09; 一、S32K344的内存布局 MEMORY {int_pflash : ORIGIN 0x00400000, LENGTH 0x003D4000 /* 4096KB - 176KB (sBAF HSE)*/int_dflash : ORIGIN 0x10000000, LENGTH 0x00020000 /* 128KB …

PhpStudy | PhpStudy 安装

关注这个软件的其他相关笔记&#xff1a;PhpStudy —— README-CSDN博客 0x01&#xff1a;Windows 系统安装 PhpStudy 附件资源 PhpStudy - CSDN 配套资源&#xff1a;phpstudy_x64_8.1.1.3.zip PhpStudy - 官网地址&#xff1a;小皮面板-好用、安全、稳定的Linux服务器面板&a…

EfficientNet(2019):基于复合缩放的自动化架构搜索高效网络!

EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks EfficientNet&#xff1a;重新思考卷积神经网络的模型扩展 论文下载地址&#xff1a; https://arxiv.org/abs/1905.11946 学习笔记参考了这位大佬&#xff1a;https://blog.csdn.net/qq_37541097/ar…

在C#中实现WebSocket的单聊和分频道聊天

在C#中实现WebSocket的单聊和分频道聊天&#xff0c;可以利用System.Net.WebSockets库。以下是如何实现这个功能的具体方案和代码。 方案概述&#xff1a; WebSocket Server&#xff1a; 通过HttpListener或ASP.NET Core来承载WebSocket服务。维护每个客户端的连接&#xff0c…

基于贝叶斯优化CNN-GRU网络的数据分类识别算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1卷积神经网络&#xff08;CNN&#xff09; 4.2 GRU网络 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 优化前&#xff1a; 优化后&#xff1a; 2.算法运行软件版…

Java网络编程、正则表达式、单例设计模式与Lombok

目录 Java网络编程、正则表达式、单例设计模式与Lombok Java网络编程 软件结构 网络基础知识 相关概念 IP地址 TCP协议和UDP协议介绍 TCP协议的三次握手和四次挥手 UDP协议编程 创建客户端 创建服务端 运行 TCP协议编程 创建客户端 创建服务端 运行 文件上传案例 创建客户端 创…

windows C++-同步数据结构与 Windows API

将并发运行时提供的同步数据结构的行为与 Windows API 提供的同步数据结构的行为进行比较。 并发运行时提供的同步数据结构遵循协作线程模型。 在协作线程模型中&#xff0c;同步基元显式将其处理资源传递给其他线程。 这与抢占式线程模型不同&#xff0c;其中处理资源由控制调…

Python | Leetcode Python题解之第434题字符串中的单词数

题目&#xff1a; 题解&#xff1a; class Solution:def countSegments(self, s):segment_count 0for i in range(len(s)):if (i 0 or s[i - 1] ) and s[i] ! :segment_count 1return segment_count

教授【优青】团队亲自指导-图解表观遗传学 | 组蛋白修饰!专业实验设计、数据分析、SCI论文辅助等全方位服务。精准高效,为农植物科研保驾护航!

教授【优青】团队亲自指导&#xff01;提供专业实验设计、数据分析、SCI论文辅助等全方位服务。精准高效&#xff0c;为医学科研保驾护航&#xff01; 专业实验外包服务&#xff0c;一站式解决您的所有需求&#xff1b; 还在犹豫&#xff1f;别让您的科研和论文停滞不前&#…

C# Socket 服务端

WPF 项目 引入 Socket using System.Net.Sockets; 声明 Socket 并创建对象等待客户端连接 开启线程等待客户端连接并接收消息 接收消息并解析 发送消息 完整代码

目标检测DOTA数据集

前言 ​ 之前对于xml格式的YOLO数据集&#xff0c;之前记录过如何用imgaug对其进行数据增强。不过DOTA数据集采用的是txt格式的旋转框标注&#xff0c;因此不能直接套用&#xff0c;只能另辟蹊径。 DOTA数据集简介 DOTA数据集全称&#xff1a;Dataset for Object deTection i…

Redis6 多线程模型

优质博文&#xff1a;IT-BLOG-CN 一、单线程的优缺点 对于一个请求操作Redis主要做3件事情&#xff1a;从客户端读取数据/解析、执行Redis命令、回写数据给客户端。所以主线程其实就是把所有操作的这3件事情串行一起执行&#xff0c;因为是基于内存&#xff0c;所以执行速度非…

区间合并算法

区间合并 区间合并就是有两个区间我们把两个区间合并成一个区间 我们来看一道题 Acwing 803 区间合并 1.题目 给定 n nn 个区间 [ l i , r i ] [li,ri][li,ri]&#xff0c;要求合并所有有交集的区间。 注意如果在端点处相交&#xff0c;也算有交集。 输出合并完成后的区间个…

C语言 | Leetcode C语言题解之第434题字符串中的单词数

题目&#xff1a; 题解&#xff1a; int countSegments(char * s){int count 0; //count用来记录单词个数for(int i0; i < strlen(s); i){ //遍历字符串 if((i 0 || s[i-1] ) && s[i] ! ) //一个…