【算法思想·数组】二维数组的花式遍历

本文参考labuladong算法小抄[二维数组的花式遍历技巧盘点]

1、顺/逆时针旋转矩阵

48. 旋转图像

【python解法】

class RotateImage90(object):
    def rotate90(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        顺时针将矩阵旋转90°
        """
        # 以主对角线为轴翻转矩阵
        m, n = len(matrix), len(matrix[0]) # 旋转矩阵只针对正方形,故 m = n
        for i in range(m):
            for j in range(i, n):
                matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
        # 将翻转后的矩阵每一行进行翻转
        for mat in matrix:
            mat.reverse()


class RotateImageMinus90:
    def rotate_minus90(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        逆时针将矩阵旋转90°
        """
        # 以副对角线为轴翻转矩阵
        m, n = len(matrix), len(matrix[0])
        for i in range(m):
            for j in range(n-i):
                matrix[i][j], matrix[m-j-1][n-i-1] = matrix[m-j-1][n-i-1], matrix[i][j]
        # 将翻转后的矩阵每一行进行翻转
        for mat in matrix:
            mat.reverse()

【算法思路】

题目很好理解,就是让你将一个二维矩阵顺时针旋转 90 度,难点在于要「原地」修改,函数签名如下:

void rotate(int[][] matrix)

如何「原地」旋转二维矩阵?稍想一下,感觉操作起来非常复杂,可能要设置巧妙的算法机制来「一圈一圈」旋转矩阵:

 

但实际上,这道题不能走寻常路,在讲巧妙解法之前,我们先看另一道谷歌曾经考过的算法题热热身:

给你一个包含若干单词和空格的字符串s,请你写一个算法,原地反转所有单词的顺序。

比如说,给你输入这样一个字符串:

s = "hello world labuladong"

你的算法需要原地反转这个字符串中的单词顺序:

s = "labuladong world hello"

常规的方式是把s按空格split成若干单词,然后reverse这些单词的顺序,最后把这些单词join成句子。但这种方式使用了额外的空间,并不是「原地反转」单词。

正确的做法是,先将整个字符串s反转

s = "gnodalubal dlrow olleh"

然后将每个单词分别反转

s = "labuladong world hello"

这样,就实现了原地反转所有单词顺序的目的。

我讲这道题的目的是什么呢?

旨在说明,有时候咱们拍脑袋的常规思维,在计算机看来可能并不是最优雅的;但是计算机觉得最优雅的思维,对咱们来说却不那么直观。也许这就是算法的魅力所在吧。

回到之前说的顺时针旋转二维矩阵的问题,常规的思路就是去寻找原始坐标和旋转后坐标的映射规律,但我们是否可以让思维跳跃跳跃,尝试把矩阵进行反转、镜像对称等操作,可能会出现新的突破口。

我们可以先将n x n矩阵matrix按照左上到右下的对角线进行镜像对称

 然后再对矩阵的每一行进行反转

发现结果就是matrix顺时针旋转 90 度的结果

// 将二维矩阵原地顺时针旋转 90 度
public void rotate(int[][] matrix) {
    int n = matrix.length;
    // 先沿对角线镜像对称二维矩阵
    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            // swap(matrix[i][j], matrix[j][i]);
            int temp = matrix[i][j];
            matrix[i][j] = matrix[j][i];
            matrix[j][i] = temp;
        }
    }
    // 然后反转二维矩阵的每一行
    for (int[] row : matrix) {
        reverse(row);
    }
}

// 反转一维数组
void reverse(int[] arr) {
    int i = 0, j = arr.length - 1;
    while (j > i) {
        // swap(arr[i], arr[j]);
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
        i++;
        j--;
    }
}

既然说道这里,我们可以发散一下,如何将矩阵逆时针旋转 90 度

思路是类似的,只要通过另一条对角线镜像对称矩阵,然后再反转每一行,就得到了逆时针旋转矩阵的结果:

class RotateImageMinus90:
    def rotate_minus90(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        逆时针将矩阵旋转90°
        """
        # 以副对角线为轴翻转矩阵
        m, n = len(matrix), len(matrix[0])
        for i in range(m):
            for j in range(n-i):
                matrix[i][j], matrix[m-j-1][n-i-1] = matrix[m-j-1][n-i-1], matrix[i][j]
        # 将翻转后的矩阵每一行进行翻转
        for mat in matrix:
            mat.reverse()

2、矩阵的螺旋遍历

54. 螺旋矩阵

【python 解法】

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        m, n = len(matrix), len(matrix[0])
        # 定义目标矩阵上下左右四个边界
        t, b, l, r = 0, m-1, 0, n-1
        res = []

        count = 0
        # 用count来计数,直到count等于m*n即停止遍历
        while count < m * n:
            # 在顶部从左向右遍历
            if count == m * n:
                return res
            for i in range(l, r+1):
                res.append(matrix[t][i])
                count += 1
            # 上边界下移
            t += 1
            # 在右侧从上到下遍历
            if count == m * n:
                return res
            for i in range(t, b+1):
                res.append(matrix[i][r])
                count += 1
            # 右边界左移
            r -= 1
            # 在底部从右向左遍历
            if count == m * n:
                return res
            for i in range(r, l-1, -1):
                res.append(matrix[b][i])
                count += 1
            # 下边界上移
            b -= 1
            # 在左侧从下到上遍历
            if count == m * n:
                return res
            for i in range(b, t-1, -1):
                res.append(matrix[i][l])
                count += 1
            # 左边界右移
            l += 1

        return res

【算法思路】

函数签名如下:

List<Integer> spiralOrder(int[][] matrix)

解题的核心思路是按照右、下、左、上的顺序遍历数组,并使用四个变量圈定未遍历元素的边界

随着螺旋遍历,相应的边界会收缩,直到螺旋遍历完整个数组:

59. 螺旋矩阵 II

【python 解法】

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        m, n = len(matrix), len(matrix[0])
        # 定义目标矩阵上下左右四个边界
        t, b, l, r = 0, m-1, 0, n-1
        res = []

        count = 0
        # 用count来计数,直到count等于m*n即停止遍历
        while count < m * n:
            # 在顶部从左向右遍历
            for i in range(l, r+1):
                if count == m*n:
                    return res
                res.append(matrix[t][i])
                count += 1
            # 上边界下移
            t += 1
            # 在右侧从上到下遍历
            for i in range(t, b+1):
                if count == m*n:
                    return res
                res.append(matrix[i][r])
                count += 1
            # 右边界左移
            r -= 1
            # 在底部从右向左遍历
            for i in range(r, l-1, -1):
                if count == m*n:
                    return res
                res.append(matrix[b][i])
                count += 1
            # 下边界上移
            b -= 1
            # 在左侧从下到上遍历
            for i in range(b, t-1, -1):
                if count == m*n:
                    return res
                res.append(matrix[i][l])
                count += 1
            # 左边界右移
            l += 1

        return res

【算法思路】

 如上题,大致也是先定好目标矩阵的上下左右四个边界,然后螺旋遍历矩阵。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值