LeetCode题目总结——二维数组
文章目录
1. 配合Map
遍历数组,同时使用Map记录已遍历过的信息。
a. 常规问题可使用HashMap
b. 若索引为整数且范围较小,可使用数组代替HashMap
c. 若索引范围进一步缩小至32或64以内,且值为bool型,可使用一个整形数代替数组(整形数的各位代替数组元素)
- 0036 - 有效的数独
使用三个map分别记录行、列、块中出现过的数字。可使用整形数作为map。 - 0073 - 矩阵置零
2. 旋转遍历
旋转遍历数组,同时进行相应操作。注意维护好当前遍历层的起止行列号(m0, m1, n0, n1)。
- 0048 - 旋转图像
关键赋值公式:matrix(j, N-1-i)=matrix(i, j) 列号变行号,行号变列号同时被N-1减。 - 0054 - 螺旋矩阵
注意避免行列重复遍历,遍历逻辑:先遍历(m0, n0 ~ n1), (m0+1 ~ m1, n1), 如果n1>n0&&m1>m0,则再遍历(m1, n1-1 ~ n0+1), (m1 ~ m0+1, n0) - 0059 - 螺旋矩阵II
3. 元素查找
3.1 一维二分查找
将二维数组展开为一维数组,然后进行常规一维二分查找,需保证二维数组展开后仍满足单调性。
-
0074 - 搜索二维矩阵
将二维数组展开为一维数组,然后进行常规一维二分查找。 -
0300 - 最长递增子序列
int lengthOfLIS(vector<int>& nums) {
vector<int> ls;
for (int n : nums) {
if (ls.size() == 0 || ls.back() < n) {
ls.emplace_back(n);
}
else {
int l = 0;
int r = ls.size() - 1;
while (l < r) {
int m = l + (r - l) / 2;
if (ls[m] >= n) {
r = m;
}
else {
l = m + 1;
}
}
ls[l] = n;
}
}
return ls.size();
}
- 周赛222 - 3 - 得到子序列的最少操作次数
由于target数组不重复,因此可以将arr中的元素转为target中的索引,从而将最长公共子序列问题转换为最长递增子序列问题。
4. 二维动态规划
二维动态规划,注意可以按照逐行进行一维动态规划,从而将空间复杂度可以从O(n2)优化至O(n)的情况。
- 0062 - 不同路径
//状态转移方程
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
//优化后
dp[j] += dp[j] + dp[j - 1]
- 0063 - 不同路径II
// 状态转移方程
if (grid[i][i])
dp[j] += dp[j] + dp[j - 1];
else
dp[j] = 0
- 0064 - 最小路径和
// 状态转移方程
dp[j] = grid[i][j] + min(dp[j], dp[j - 1])
- 0085 - 最大矩形
// 状态转移方程
if (matrix[i][j] == '1') {
height[j] = height[j] + 1;
}
else {
height[j] = 0;
}
if (matrix[i][j] == '1') {
left[j] = max(left[j], cur_left);
}
else {
left[j] = 0;
cur_left = j + 1;
}
if (matrix[i][j] == '1') {
right[j] = min(right[j], cur_right);
}
else {
right[j] = N - 1;
cur_right = j - 1;
}
5. 状态压缩动态规划
对行列中较短者进行状态压缩,以该维度的所有状态为状态空间,沿另一维度进行动态规划。常用于解决元素排布问题(向二维网格中添加元素,添加的元素会对周围空间产生影响)。
- 1349 - 参加考试的最大学生数
- 周赛215-03 - 最大化网格幸福感
6. 距离矩阵
计算每个二维网格距离最近有效区域的距离。
7. 最大面积矩形
在0/1矩阵中寻找由1组成的最大矩形。
- 0084 - 柱状图中最大矩形
维护最大栈,寻找某元素两侧首先出现小于该元素的位置a,b,选择height * (b - a - 1)中的最大值。
// 栈维护逻辑
while (!stack.empty() && heights[i] < heights[stack.top()]) {
int height = heights[stack.top()];
stack.pop();
res = max<int>(res, height * (i - stack.top() - 1));
}
stack.push(i);
-
0085 - 最大矩形
可转换为多次0084问题进行求解。
先对每个位置进行计算:以该位置结束时,该列的最大连续1的高度;然后按每一行计算以该行为止的最大1矩形(栈求解)。 -
周赛224 - 5655 - 重新排列后的最大子矩阵
0085为固定的0/1矩阵,而该题允许对矩阵进行按列重排。
先对每个位置进行计算:以该位置结束时,该列的最大连续1的高度;然后对每一行按高度进行排序,以该行为止的最大1矩形(贪心求解)。
int largestSubmatrix(vector<vector<int>>& matrix) {
int M = matrix.size();
int N = matrix[0].size();
// 计算连续1的高度
for (int i = 1; i < M; ++i) {
for (int j = 0; j < N; ++j) {
if (matrix[i][j] == 1) {
matrix[i][j] = matrix[i - 1][j] + 1;
}
}
}
// 按列求最大矩形
int res = 0;
for (int i = 0; i < M; ++i) {
// 高度排序
sort(matrix[i].begin(), matrix[i].end());
int height = INT_MAX;
for (int j = N - 1; j >= 0; --j) {
// 当前有效高度
height = min(height, matrix[i][j]);
if (height == 0) break;
// 贪心选取
res = max(res, height * (N - j));
}
}
return res;
}
a.1 字符串数组之字母异位词
将每一段字符串转换为key值,相同key值的字符串字母相同,但排列不同的,即字母异位词。有以下几种转换方式:
通用做法,时间nlogn,空间n:
for s in strs:
key = s.sort()
优化做法2,时间n,空间2n:
for s in strs:
count = [0 for _ in range(26)]
for c in s:
count[ord(c) - ord('a')] += 1
key = ''
for i in range(26):
key += '#' + str(count[i])
优化做法3,时间n,空间1,但字符串不能太长:
primes =[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101]
for s in strs:
key = 1
for c in s:
key *= primes[ord(c) - ord('a')]
- 0049 - 字母异位词分组
b.1 区间问题之区间合并
按区间起点排序,记录最晚的区间终点。对于整数区间且数值不大的情况,可以使用桶排序进行加速。
- 0056 - 区间合并
- 0057 - 插入区间