二维矩阵转换(多种遍历方法)

简介: 二维矩阵转换(多种遍历方法)

1.螺旋矩阵(54-中)



题目描述:给你一个 mn 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。


示例

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


思路:直接模拟按层填充,定义左右上下边界,循环遍历矩阵。注意:如果是方阵,就可以直接退出循环,但是,如果存在l > r || t > b,则证明最后一次不是圈,是一行或者一列,这时我们可以直接终止循环。


代码

public List<Integer> spiralOrder(int[][] matrix) {
    List<Integer> ans = new ArrayList<>();
    int m = matrix.length, n = matrix[0].length;
    int l = 0, r = n - 1, t = 0, b = m - 1;
    while (l <= r && t <= b) {
        for (int j = l; j <= r; ++j) {
            ans.add(matrix[t][j]);
        }
        t++;
        for (int i = t; i <= b; ++i) {
            ans.add(matrix[i][r]);
        }
        r--;
        if (l > r || t > b) {
            break;
        }
        for (int j = r; j >= l; --j) {
            ans.add(matrix[b][j]);
        }
        b--;
        for (int i = b; i >= t; --i) {
            ans.add(matrix[i][l]);
        }
        l++;
    }
    return ans;
}


2.螺旋矩阵II(59-中)



题目描述:生成1-n^2的所有元素,且元素按顺时针螺旋排列的正方形矩阵


示例

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


思路:与上题思路相同,区别是退出循环机制,采用num < tar,这样避免了判断奇偶的情况。


代码

public int[][] generateMatrix(int n) {
    int l = 0, r = n - 1, t = 0, b = n - 1;
    int[][] matrix = new int[n][n];
    int num = 1;
    while (l <= r && t <= b) {
        for (int j = l; j <= r; ++j) {
            matrix[t][j] = num++;
        }
        t++;
        for (int i = t; i <= b; ++i) {
            matrix[i][r] = num++;
        }
        r--;
        for (int j = r; j >= l; --j) {
            matrix[b][j] = num++;
        }
        b--;
        for (int i = b; i >= t; --i) {
            matrix[i][l] = num++;
        }
        l++;
    }
    return matrix;
}


3.对角线打印矩阵(498-中)



题目描述:给定一个含有 M x N 个元素的矩阵(M 行,N 列),请以对角线遍历的顺序返回这个矩阵中的所有元素,对角线遍历如下图所示。


示例

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


思路:两种方向的变更,右上到左下,依次交换方向,将结果加入数组。流程如下:


  • 确定循环次数,即方向变更的次数 m + n - 1
  • 从0开始,偶数次数为右上,奇数次数为右下(通过取余实现)
  • 最后确定两个方向具体实现,比如右上方向,设横坐标x, 纵坐标y,一般变化(x--, y++),但是注意边界处理,即上图中1 - 2, 3 - 6,对应不同的边界处理。左下方向类似。具体见代码


代码

class Solution {
    public int[] findDiagonalOrder(int[][] mat) {
        int rowLength = mat.length;
        int colLength = mat[0].length;
        int[] ans = new int[rowLength * colLength];
        int loop = rowLength + colLength - 1;
        int x = 0;
        int y = 0;
        int index = 0;
        for (int i = 0; i < loop; i++) {
            if (i % 2 == 0) {
                while (x >= 0 && y < colLength) {
                    ans[index++] = mat[x][y];
                    x--;
                    y++;
                }
                if (y < colLength) {
                    // 对应1-2边界
                    x++;
                } else {
                    // 对应3-6边界,ps:需要将上一步while中的x和y位置进行修正(x加1, y不变)
                    x += 2; 
                    y--;
                }
            } else {
                while (x < rowLength && y >= 0) {
                    ans[index++] = mat[x][y];
                    x++;
                    y--;
                }
                if (x < rowLength) {
                    // 对应4-7边界
                    y++;
                } else {
                    // 对应8-9边界
                    x--;
                    y += 2;
                }
            }
        }
        return ans;
    }
}


4.旋转图像(48-中)



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


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


示例

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


思路


法1:直接模拟旋转,可以看成一圈一圈的移动。注意:内层循环,为了避免奇数情况,每一圈可能有一个不移动,我们对n进行加1,可以自行举例n = 2 和 n = 3的情况。


法2:先水平翻转,然后主对角线翻转。


代码

// 代码1
public void rotate(int[][] matrix) {
    int n = matrix.length;
    for (int i = 0; i < n / 2; i++) {
        for (int j = 0; j < (n + 1) / 2; j++) {
            int tmp = matrix[i][j];
            matrix[i][j] = matrix[n - j - 1][i];
            matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
            matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
            matrix[j][n - i - 1] = tmp;
        }
    }
}
// 代码2
public void rotate(int[][] matrix) {
    int n = matrix.length;
    // 水平翻转
    for (int i = 0; i < n / 2; i++) {
        for (int j = 0; j < n; j++) {
            int temp = matrix[i][j];
            matrix[i][j] = matrix[n - i - 1][j];
            matrix[n - i - 1][j] = temp;
        }
    }
    // 主对角线翻转
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < i; j++) {
            int temp = matrix[i][j];
            matrix[i][j] = matrix[j][i];
            matrix[j][i] = temp;
        }
    }
}


5.搜索二维矩阵(74-中)



题目描述:编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:


  • 每行中的整数从左到右按升序排列。
  • 每行的第一个整数大于前一行的最后一个整数。


示例

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true


思路:本题关键解题关键是矩阵有序,我们可以从右上角或者左下角出发,缩小数据搜索范围,遍历寻找目标值。


以右上角为例当前遍历左边的元素比当前值小,当前遍历下边的元素比当前值大。


最优解为二分查找:本题中行尾和行首连接,也具有单调性,故可将二维矩阵转成一维矩阵去做。


代码

// 直接搜索,时间复杂度:O(m + n)
public boolean searchMatrix(int[][] matrix, int target) {
    int i = 0, j = matrix[0].length - 1;
    while (i < matrix.length && j > -1) {
        if (matrix[i][j] == target) return true;
        else if (matrix[i][j] > target) {
            j--;
        } else {
            i++;
        }
    }
    return false;
}
// 二分查找,时间复杂度:O(log(mn))
public boolean searchMatrix(int[][] matrix, int target) {
    int m = matrix.length, n = matrix[0].length;
    int i = 0, j = m * n - 1;
    while (i < j) {
        int mid = i + ((j - i) >> 1);
        if (matrix[mid / n][mid % n] < target) {
            i = mid + 1;
        } else {
            j = mid;
        }
    }
    return matrix[i / n][i % n] == target;
}


6.搜索二维矩阵II(240-中)



题目描述:在有序矩阵中寻找指定数值。


  • 每行的元素从左到右升序排列。
  • 每列的元素从上到下升序排列。


示例

输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true


思路:直接搜索基本思路同上。但是二分查找则不同,上题我们首先在第一列小于目标值的第一个数,然后再查找目标行,但是由于本题数据排布整体不是严格单调的!


利用有序的性质,我们可以一行一行的进行二分查找(也可以一列一列的)


  • 当某一行的第一个元素都大于target了,那么当前行和之后的行都不用考虑了
  • 如果当前行的最后一个元素小于target,当前行排除,直接进入下一行。


代码

// 直接搜索,时间复杂度:O(m + n)
public boolean searchMatrix(int[][] matrix, int target) {
    int i = 0, j = matrix[0].length - 1;
    while (i < matrix.length && j > -1) {
        if (matrix[i][j] == target) return true;
        else if (matrix[i][j] > target) {
            j--;
        } else {
            i++;
        }
    }
    return false;
}
// 二分查找,时间复杂度:O(mlogn)
public boolean searchMatrix(int[][] matrix, int target) {
    int m = matrix.length, n = matrix[0].length;
    for (int i = 0; i < m; ++i) {
        if (matrix[i][0] > target) {
            break;
        } 
        if (matrix[i][n - 1] < target) {
            continue;
        }
        int col = binarySearch(matrix[i], target);
        if (col != -1) {
            return true;
        }
    }
    return false;
}
// 二分查找目标值的索引(类似源码)
public int binarySearch(int[] arr, int target) {
    int i = 0, j = arr.length - 1;
    while (i <= j) {
        int mid = (i + j) >>> 1;
        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] < target) {
            i = mid + 1;
        } else {
            j = mid - 1;
        }
    }
    return -1;
}


7.矩阵置零(73-中)



题目描述:给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法(在函数输入矩阵上直接修改)。进阶:


  • 一个直观的解决方案是使用  O(mn) 的额外空间,但这并不是一个好的解决方案。
  • 一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
  • 你能想出一个仅使用常量空间的解决方案吗?


示例

输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
输出:[[1,0,1],[0,0,0],[1,0,1]]


思路:本题的关键点:如果在输入矩阵上原地修改把行列修改成了 0,那么再遍历到 0 的时候就不知道是原本数组中的 0 还是我们修改得到的 0。所有的解法都是为了解决该问题。


方案1:遍历两遍矩阵,第一遍记录哪些行哪些列有0,第二遍置0。使用两个set进行记录,空间复杂度O(m + n)


方案2:核心:将第一行和第一列作为标志位,为了避免是由于第一行和第一列本来就有0造成的置0,我们需要对第一行第一列单独进行判断,只需要加两个boolean类型变量进行判断即可。


对于方案2优化,直接使用一个标志位,简化代码,但是不如两个标志位好理解。你可能会说一个不是造成了覆盖吗(原先第一个行某个位置不是0,现在是0)。


假设该列没有0,那么第一行对应位置一定不是0,如果是0,那么可能是本身,也可能是下边0导致修改,如何区别呢?正序置0我们没有办法区别,逆序置0即使是覆盖,那么该位置最终也一定是0。


代码

// 空间复杂度:O(m + n)
public void setZeroes(int[][] matrix) {
    int m = matrix.length, n = matrix[0].length;
    Set<Integer> rowZero = new HashSet<>();
    Set<Integer> colZero = new HashSet<>();
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            if (matrix[i][j] == 0) {
                rowZero.add(i);
                colZero.add(j);
            }
        }
    }
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            if (rowZero.contains(i) || colZero.contains(j)) {
                matrix[i][j] = 0;
            }
        }
    }
}
// 标记法,空间复杂度:O(1)
public void setZeroes(int[][] matrix) {
    int m = matrix.length, n = matrix[0].length;
    boolean row0Flag = false;
    boolean col0Flag = false;
    for (int j = 0; j < n; ++j) {
        if (matrix[0][j] == 0) {
            row0Flag = true;
            break;
        }
    }
    for (int i = 0; i < m; ++i) {
        if (matrix[i][0] == 0) {
            col0Flag = true;
            break;
        }
    }
    // 第一行第一列作为标志位
    for (int i = 1; i < m; ++i) {
        for (int j = 1; j < n; ++j) {
            if (matrix[i][j] == 0) {
                matrix[i][0] = matrix[0][j] = 0;
            }
        }
    }
    for (int i = 1; i < m; ++i) {
        for (int j = 1; j < n; ++j) {
            if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                matrix[i][j] = 0;
            }
        }
    }
    if (row0Flag) {
        for (int j = 0; j < n; ++j) {
            matrix[0][j] = 0;
        }
    }
    if (col0Flag) {
        for (int i = 0; i < m; ++i) {
            matrix[i][0] = 0;
        }
    }
}
// 代码优化:一个标志
public void setZeroes(int[][] matrix) {
    int m = matrix.length, n = matrix[0].length;
    boolean col0Flag = false;
     for (int i = 0; i < m; ++i) {
        if (matrix[i][0] == 0) col0Flag = true;
        for (int j = 1; j < n; ++j) {
            if (matrix[i][j] == 0) {
                matrix[i][0] = matrix[0][j] = 0;
            }
        }
    }
    for (int i = m - 1; i >= 0; --i) {
        for (int j = n - 1; j >= 1; --j) {
            if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                matrix[i][j] = 0;
            }
        }
        if (col0Flag) matrix[i][0] = 0;
    }
}


相关文章
|
6月前
|
存储 机器学习/深度学习 算法
【算法训练-数组 三】【数组矩阵】螺旋矩阵、旋转图像、搜索二维矩阵
【算法训练-数组 三】【数组矩阵】螺旋矩阵、旋转图像、搜索二维矩阵
87 0
|
14天前
矩阵转换
【10月更文挑战第30天】矩阵转换。
22 3
|
5月前
|
存储 数据可视化 算法
原始边列表转换为邻接矩阵
【6月更文挑战第23天】在图论和网络分析中,图由节点和边构成,可以用邻接矩阵表示。Python代码展示了如何从边列表`(0, 1), (0, 2), (1, 2), (2, 3)`转换成邻接矩阵,涉及有向/无向图、权重处理及稀疏矩阵优化。此外,还包括了使用NetworkX库进行图可视化以及将邻接矩阵逆向转换为边列表。这些方法在处理大规模图数据时尤其重要,如社交网络分析和交通规划。
48 1
|
4月前
|
存储 算法 Python
稀疏矩阵是矩阵中大部分元素为零的矩阵。
稀疏矩阵是矩阵中大部分元素为零的矩阵。
|
6月前
19.把1~100存到二维数组a[10][10]中,并按二维矩阵形式输出
19.把1~100存到二维数组a[10][10]中,并按二维矩阵形式输出
42 0
创建二维数组和矩阵
在Julia中,可以使用逗号或两个冒号创建二维数组和矩阵。例如,`[1 2 3 4]`和`[1;; 2;; 3;; 4]`创建1x4矩阵。添加分号`;`创建多行,如`[1 2; 3 4]`形成2x2矩阵。使用冒号和空格,如`[1:2 3:4]`也可得到2x2矩阵。通过嵌入相同长度的一维数组,如`[[1,2] [3,4] [5,6]]`,可构建2x3矩阵。利用分号和空格能创建不同形状的矩阵,如2x3和3x2矩阵。
|
6月前
|
机器学习/深度学习 存储 人工智能
利用前缀和计算二维矩阵子矩阵的和
利用前缀和计算二维矩阵子矩阵的和
67 0
|
机器学习/深度学习 存储 人工智能
第3章 数组与矩阵——3.2 矩阵操作
第3章 数组与矩阵——3.2 矩阵操作
第3章 数组与矩阵——3.3 矩阵元素的运算(1)
第3章 数组与矩阵——3.3 矩阵元素的运算(1)