旋转
48. 旋转图像 mid
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
画一个
_|
看看怎么翻转
class Solution {
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;
}
}
// y=x 翻转
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;
}
}
}
}
转圈
54. 螺旋矩阵 mid
剑指 Offer 29. 顺时针打印矩阵 easy
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
控制好 4 个方向的边界
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
int m = matrix.length, n = matrix[0].length;
List<Integer> res = new ArrayList<>();
int u = 0, d = m - 1, l = 0, r = n - 1;
while (true) {
for (int i = l; i <= r; i++) res.add(matrix[u][i]);
if (++u > d) break;
for (int i = u; i <= d; i++) res.add(matrix[i][r]);
if (--r < l) break;
for (int i = r; i >= l; i--) res.add(matrix[d][i]);
if (--d < u) break;
for (int i = d; i >= u; i--) res.add(matrix[i][l]);
if (++l > r) break;
}
return res;
}
}
修改
73. 矩阵置零 mid
给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用 原地 算法。
解法 1: 2 个数组标记
二进制压缩
class Solution {
public void setZeroes(int[][] matrix) {
BitSet ir = new BitSet();
BitSet ic = new BitSet();
int r = matrix.length;
int c = matrix[0].length;
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
if (matrix[i][j] == 0) {
ir.set(i);
ic.set(j);
}
}
}
for (int i = 0; i < r; i++) {
if (ir.get(i)) Arrays.fill(matrix[i], 0);
}
for (int j = 0; j < c; j++) {
if (ic.get(j)) {
for (int i = 0; i < r; i++) {
matrix[i][j] = 0;
}
}
}
}
}
解法 2: 2 个额外标记+数组自身标记
class Solution {
public void setZeroes(int[][] m) {
int r = m.length, c = m[0].length;
boolean flagCol0 = false, flagRow0 = false;
for (int i = 0; i < r; i++) {
if (m[i][0] == 0) {
flagCol0 = true;
break;
}
}
for (int j = 0; j < c; j++) {
if (m[0][j] == 0) {
flagRow0 = true;
break;
}
}
for (int i = 1; i < r; i++) {
for (int j = 1; j < c; j++) {
if (m[i][j] == 0) m[i][0] = m[0][j] = 0;
}
}
for (int i = 1; i < r; i++) {
for (int j = 1; j < c; j++) {
if (m[i][0] == 0 || m[0][j] == 0) m[i][j] = 0;
}
}
if (flagCol0) for (int i = 0; i < r; i++) m[i][0] = 0;
if (flagRow0) for (int j = 0; j < c; j++) m[0][j] = 0;
}
}
解法 3: 1 个额外标记+数组自身标记
用 m[0][0] 标记首行
class Solution {
public void setZeroes(int[][] m) {
int r = m.length, c = m[0].length;
// 第0列含0标记
boolean flagCol0 = false;
for (int i = 0; i < r; i++) {
if (m[i][0] == 0) flagCol0 = true;
for (int j = 1; j < c; j++) { // 第0列本身就是标记
if (m[i][j] == 0) m[i][0] = m[0][j] = 0;
}
}
// 首行首列要最后更新
for (int i = r - 1; i >= 0; i--) {
for (int j = 1; j < c; j++) {
if (m[i][0] == 0 || m[0][j] == 0) m[i][j] = 0;
}
if (flagCol0) m[i][0] = 0;
}
}
}
找值
240. 搜索二维矩阵 II mid
剑指 Offer 04. 二维数组中的查找
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:
- 每行的元素从左到右升序排列。
- 每列的元素从上到下升序排列。
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int r = matrix.length, c = matrix[0].length;
int i = 0, j = c - 1;
while (i < r && j >= 0) {
if (matrix[i][j] == target) return true;
if (matrix[i][j] < target) i++;
else j--;
}
return false;
}
}
378. 有序矩阵中第 K 小的元素 mid
给你一个 n x n 矩阵 matrix, 其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。 请注意,它是 排序后 的第 k 小元素,而不是第 k 个 不同 的元素。
你必须找到一个内存复杂度优于 O(n2) 的解决方案。
- 你能否用一个恒定的内存(即 O(1) 内存复杂度)来解决这个问题?
- 你能在 O(n) 的时间复杂度下解决这个问题吗?
求第 k 个元素有 3 种常用方法, 快排, 归并(堆排), 二分猜答案
解法 二分猜答案
找元素 e, 满足: 小于 e 的元素的数量 < k, 小于等于 e 的元素的数量 >= k
(小于等于 e 的元素数量 >= k) 的左边界
为什么收敛的边界值 mid 一定在矩阵中, 因为满足条件(小于等于 e 的元素数量 >= k)的左边界一定是矩阵中的值
cnt 可以按行计数, 从上往下越来越短
class Solution {
public int kthSmallest(int[][] matrix, int k) {
int r = matrix.length, c = matrix[0].length;
int l = matrix[0][0], h = matrix[r - 1][c - 1];
while (l < h) {
// 虚拟 1 个 mid
int mid = l + (h - l >> 1);
int cnt = cnt(matrix, mid);
if (cnt < k)
l = mid + 1;
else
h = mid;
}
return l;
}
// 统计<=mid 的元素数量
private int cnt(int[][] matrix, int mid) {
int i = 0;
int j = matrix[0].length - 1;
int res = 0;
// 按行统计, 下一行比上一行短
while (i < matrix.length && j >= 0) {
if (matrix[i][j] <= mid) {
res += j + 1;
i++;
} else j--;
}
return res;
}
}
面积
304. 二维区域和检索 - 矩阵不可变 mid
LCR 013. 二维区域和检索 - 矩阵不可变
给定一个二维矩阵 matrix,以下类型的多个请求:
- 计算其子矩形范围内元素的总和,该子矩阵的 左上角 为 (row1, col1), 右下角 为 (row2, col2) 。
实现 NumMatrix 类:
- NumMatrix(int[][] matrix)给定整数矩阵 matrix 进行初始化
- sumRegion 返回 左上角 (row1, col1)、右下角(row2, col2) 所描述的子矩阵的元素 总和 。
解法 1 单行前缀和
记录每一行的前缀和
class NumMatrix {
private int[][] arr;
public NumMatrix(int[][] matrix) {
int r = matrix.length, c = matrix[0].length;
arr = new int[r][c + 1];
for (int i = 0; i < r; i++) {
for (int j = 1; j <= c; j++) {
arr[i][j] = arr[i][j - 1] + matrix[i][j - 1];
}
}
}
public int sumRegion(int row1, int col1, int row2, int col2) {
int sum = 0;
for (int i = row1; i <= row2; i++) {
sum += arr[i][col2 + 1] - arr[i][col1];
}
return sum;
}
}
解法 2 二维前缀和
记录矩阵的面积
class NumMatrix {
private int[][] sum;
public NumMatrix(int[][] matrix) {
int r = matrix.length, c = matrix[0].length;
sum = new int[r + 1][c + 1];
for (int i = 0; i < r; i++) {
int colSum = 0;
for (int j = 0; j < c; j++) {
colSum += matrix[i][j];
// 上一行+colSum
sum[i + 1][j + 1] = sum[i][j + 1] + colSum;
}
}
}
public int sumRegion(int row1, int col1, int row2, int col2) {
return sum[row2 + 1][col2 + 1] + sum[row1][col1] - sum[row1][col2 + 1] - sum[row2 + 1][col1];
}
}
重建
566. 重塑矩阵 easy
在 MATLAB 中,有一个非常有用的函数 reshape ,它可以将一个 m x n 矩阵重塑为另一个大小不同(r x c)的新矩阵,但保留其原始数据。
给你一个由二维数组 mat 表示的 m x n 矩阵,以及两个正整数 r 和 c ,分别表示想要的重构的矩阵的行数和列数。
重构后的矩阵需要将原始矩阵的所有元素以相同的 行遍历顺序 填充。
如果具有给定参数的 reshape 操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。
根据 m,n 求出第 idx 个
class Solution {
public int[][] matrixReshape(int[][] nums, int r, int c) {
int m = nums.length, n = nums[0].length;
if (m * n != r * c) return nums;
int[][] reshapedMatrix = new int[r][c];
for (int i = 0; i < r * c; i++) {
reshapedMatrix[i / c][i % c] = nums[i / n][i % n];
}
return reshapedMatrix;
}
}
特殊矩阵
766. 托普利茨矩阵 easy
给你一个 m x n 的矩阵 matrix 。如果这个矩阵是托普利茨矩阵,返回 true ;否则,返回 false 。
如果矩阵上每一条由左上到右下的对角线上的元素都相同,那么这个矩阵是 托普利茨矩阵 。
class Solution {
public boolean isToeplitzMatrix(int[][] matrix) {
int m = matrix.length, n = matrix[0].length;
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (matrix[i][j] != matrix[i - 1][j - 1]) {
return false;
}
}
}
return true;
}
}