LeetCode题目总结——二维数组

本文总结了LeetCode中涉及二维数组的题目,包括配合Map处理、旋转遍历、元素查找、动态规划等策略,并提供了具体题目的解题思路,如矩阵旋转、一维二分查找、最大矩形等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LeetCode题目总结——二维数组

1. 配合Map

遍历数组,同时使用Map记录已遍历过的信息。
a. 常规问题可使用HashMap
b. 若索引为整数且范围较小,可使用数组代替HashMap
c. 若索引范围进一步缩小至32或64以内,且值为bool型,可使用一个整形数代替数组(整形数的各位代替数组元素)

  1. 0036 - 有效的数独
    使用三个map分别记录行、列、块中出现过的数字。可使用整形数作为map。
  2. 0073 - 矩阵置零

2. 旋转遍历

旋转遍历数组,同时进行相应操作。注意维护好当前遍历层的起止行列号(m0, m1, n0, n1)。

  1. 0048 - 旋转图像
    关键赋值公式:matrix(j, N-1-i)=matrix(i, j) 列号变行号,行号变列号同时被N-1减。
  2. 0054 - 螺旋矩阵
    注意避免行列重复遍历,遍历逻辑:先遍历(m0, n0 ~ n1), (m0+1 ~ m1, n1), 如果n1>n0&&m1>m0,则再遍历(m1, n1-1 ~ n0+1), (m1 ~ m0+1, n0)
  3. 0059 - 螺旋矩阵II

3. 元素查找

3.1 一维二分查找

将二维数组展开为一维数组,然后进行常规一维二分查找,需保证二维数组展开后仍满足单调性。

  1. 0074 - 搜索二维矩阵
    将二维数组展开为一维数组,然后进行常规一维二分查找。

  2. 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();
}
  1. 周赛222 - 3 - 得到子序列的最少操作次数
    由于target数组不重复,因此可以将arr中的元素转为target中的索引,从而将最长公共子序列问题转换为最长递增子序列问题。

4. 二维动态规划

二维动态规划,注意可以按照逐行进行一维动态规划,从而将空间复杂度可以从O(n2)优化至O(n)的情况。

  1. 0062 - 不同路径
//状态转移方程
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
//优化后
dp[j] += dp[j] + dp[j - 1]
  1. 0063 - 不同路径II
// 状态转移方程
if (grid[i][i])
 	dp[j] += dp[j] + dp[j - 1];
else
	dp[j] = 0
  1. 0064 - 最小路径和
// 状态转移方程
dp[j] = grid[i][j] + min(dp[j], dp[j - 1])
  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. 状态压缩动态规划

对行列中较短者进行状态压缩,以该维度的所有状态为状态空间,沿另一维度进行动态规划。常用于解决元素排布问题(向二维网格中添加元素,添加的元素会对周围空间产生影响)。

  1. 1349 - 参加考试的最大学生数
  2. 周赛215-03 - 最大化网格幸福感

6. 距离矩阵

计算每个二维网格距离最近有效区域的距离。

7. 最大面积矩形

在0/1矩阵中寻找由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);  
  1. 0085 - 最大矩形
    可转换为多次0084问题进行求解。
    先对每个位置进行计算:以该位置结束时,该列的最大连续1的高度;然后按每一行计算以该行为止的最大1矩形(栈求解)。

  2. 周赛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')]
  1. 0049 - 字母异位词分组

b.1 区间问题之区间合并

按区间起点排序,记录最晚的区间终点。对于整数区间且数值不大的情况,可以使用桶排序进行加速。

  1. 0056 - 区间合并
  2. 0057 - 插入区间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值