二维数组的花式遍历技巧

二维数组的花式遍历技巧

在算法和数据结构中,二维数组(或矩阵)的遍历经常出现在各种题目中,尤其是涉及图像处理、矩阵变换、螺旋排序等场景。本文将基于几个经典的题目,分析二维数组的遍历技巧,并提供详细的解题思路和代码实现。

151. 反转字符串中的单词

给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

**注意:**输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

示例 1:

输入:s = "the sky is blue"
输出:"blue is sky the"

示例 2:

输入:s = "  hello world  "
输出:"world hello"
解释:反转后的字符串中不能存在前导空格和尾随空格。

示例 3:

输入:s = "a good   example"
输出:"example good a"
解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。
解题思路:
  1. 去除多余空格:首先需要去掉字符串中的前导空格、尾随空格和多余的空格,确保单词之间只有一个空格。
  2. 反转单词顺序:我们可以将字符串按空格分割为单词,反转单词的顺序,再将它们按单个空格连接起来。
代码实现:
cpp 复制代码
class Solution {
public:
    string reverseWords(string s) {
        string s1;
        string s2;
        //从后往前找相当于把整个字符串取反一次
        for (int i=s.size()-1; i>=0; i--){
            if (s[i]==' '){
                if (!s1.empty()){
                    reverse(s1.begin(),s1.end());
                    if(!s2.empty()) {s2+=' ';}
                    s2 += s1;
                    s1.clear();
                }
            }
            else{
                s1 +=s[i];
            }
        }
        // 处理最后一个单词
        if (!s1.empty()) {
            reverse(s1.begin(), s1.end());
             if(!s2.empty()) {s2+=' ';}
            s2 += s1;
        }
        return s2;
    }
};
关键点:
  • 反转单词 :使用 reverse() 对每个单词进行反转。
  • 避免多余空格 :通过检查是否有非空的 s1,确保不会将多余的空格添加到结果字符串中。

61. 旋转链表

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

示例 1:

输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]

示例 2:

输入:head = [0,1,2], k = 4
输出:[2,0,1]
解题思路:
  1. 计算链表长度:遍历链表,统计节点个数。
  2. 计算新的头节点 :将 k 对链表长度取余,得到实际需要旋转的步数。
  3. 旋转链表:找到旋转后的新的头节点,将链表的尾节点与头节点连接起来,然后切断原有的链接。
代码实现:
cpp 复制代码
class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if(k==0||head==nullptr) return head;
        ListNode* p=head;
        int count=0;
        while(p!=nullptr){
            count++;
            p=p->next;
        }
        int num = count-(k % count);
        ListNode* q=head;
        ListNode* l=head;
        for(int i=0;i<num-1;i++){
            q=q->next;
        }
        for(int j=0;j<count-1;j++){
            l=l->next;
        }         
        l->next=head;
        ListNode* newhead=q->next;
        q->next=nullptr;
        return newhead;
    }
};
关键点:
  • 链表长度:在旋转之前,先计算链表的长度,避免不必要的旋转。
  • 链表断开:旋转后需要切断原链表的连接,将新的头节点与原链表的尾部连接。

48. 旋转图像

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]

示例 2:

输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]
解题思路:
  1. 矩阵转置 :首先对矩阵进行转置,矩阵的转置是将 matrix[i][j]matrix[j][i] 交换。
  2. 水平翻转:转置完成后,每一行进行反转即可实现顺时针旋转。
代码实现:
cpp 复制代码
class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n=matrix.size();
        for(int i=0; i<n; i++){
            for(int j=i; j<n; j++){//只对称调换上半部分
                int temp = matrix[i][j];
                matrix[i][j]=matrix[j][i];
                matrix[j][i]=temp;
            }
        }
        for (int i = 0; i < n; i++) {
            reverse(matrix[i].begin(), matrix[i].end());
        }
    }
};
关键点:
  • 原地操作:通过转置矩阵和反转每一行,实现了原地旋转。
  • 空间复杂度:不使用额外空间,避免了不必要的空间浪费。

59. 螺旋矩阵 II

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

示例 1:

输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]

示例 2:

输入:n = 1
输出:[[1]]
解题思路:
  1. 四个边界 :设置 topbottomleftright 四个变量来表示矩阵的边界。
  2. 填充数字:从左到右、从上到下、从右到左、从下到上依次填充数字。
  3. 调整边界:每次填充完一个方向后,调整对应的边界,直到所有位置都被填充完。
代码实现:
cpp 复制代码
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        int top=0 , bottom=n-1, left=0, right= n-1;
        int count=1;
        vector<vector<int>> result(n,vector<int>(n,0));//这个一定要初始化,不然报错
        while(top<=bottom && left<=right){
            if(top<=bottom){
                for(int i=left; i<=right; i++){
                    result[top][i]=count++;
                }
                top++;
            }
            if(left<=right){
                for(int j=top; j<=bottom; j++){
                    result[j][right]=count++;
                }
                right--;
            }
            if(top<=bottom){
                for(int i=right; i>=left; i--){
                    result[bottom][i]=count++;
                }
                bottom--;
            }
            if(left<=right){
                for(int j=bottom; j>=top; j--){
                    result[j][left]=count++;
                }
                left++;
            }
        }
        return result;
    }
};
关键点:
  • 螺旋填充:通过控制四个边界,逐步填充矩阵。
  • 边界更新:每填充完一轮之后,更新边界,确保填充的范围逐渐缩小。

总结

在二维数组的遍历过程中,常见的技巧包括:

  • 通过四个边界控制遍历范围:例如,螺旋矩阵和旋转矩阵的处理都依赖于逐步缩小的四个边界。
  • 转置和反转:旋转矩阵问题通过先进行转置,然后再对行进行反转实现顺时针旋转。
  • 分割与拼接:反转字符串中的单词的题目通过分割、反转和拼接来实现对字符串的转换。

这些技巧不仅提高了代码的效率,也有助于我们在面对类似问题时能够迅速找到解决方法。

相关推荐
唐诺4 小时前
几种广泛使用的 C++ 编译器
c++·编译器
XH华4 小时前
初识C语言之二维数组(下)
c语言·算法
南宫生5 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_5 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
落魄君子5 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
冷眼看人间恩怨5 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
菜鸡中的奋斗鸡→挣扎鸡5 小时前
滑动窗口 + 算法复习
数据结构·算法
红龙创客5 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin5 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
郭wes代码5 小时前
Cmd命令大全(万字详细版)
python·算法·小程序