1. 移动零问题
问题描述
输入:nums = [0,1,0,3,12]输出:[1,3,12,0,0]
输入:nums = [0]输出:[0]
解题思路:双指针技巧
- 快指针:遍历整个数组,寻找非零元素
- 慢指针:指向下一个非零元素应该放置的位置
代码实现
class Solution {
public void moveZeroes(int[] nums) {
int n = nums.length;
int slow = 0; // 慢指针指向非0元素应放置的位置
// 遍历数组,将非0元素移动到正确位置
for (int fast = 0; fast < n; fast++) {
if (nums[fast] != 0) {
nums[slow] = nums[fast];
slow++;
}
}
// 将剩余位置填充为0
for (int i = slow; i < n; i++) {
nums[i] = 0;
}
}
}
算法分析
- 时间复杂度:O(n),只需遍历数组两次
- 空间复杂度:O(1),只使用了常数级别的额外空间
- 优化效果:相比暴力解法,大大减少了元素交换次数
进阶思考
public void moveZeroesOptimized(int[] nums) {
for (int i = 0, j = 0; i < nums.length; i++) {
if (nums[i] != 0) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
j++;
}
}
}
2. 丢失的数字
问题描述
给定一个包含 [0, n] 中 n 个数的数组 nums,找出 [0, n] 这个范围内没有出现在数组中的那个数。
示例说明:
示例 1:
输入:nums = [3, 0, 1]
输出:2
解释:n = 3,所有的数字都在范围 [0, 3] 内。2是丢失的数字。
示例 2:
输入:nums = [0, 1]
输出:2
解释:n = 2,所有的数字都在范围 [0, 2] 内。2是丢失的数字。
示例 3:
输入:nums = [9, 6, 4, 2, 3, 5, 7, 0, 1]
输出:8
解释:n = 9,所有的数字都在范围 [0, 9] 内。8是丢失的数字。
解题思路:排序检查法
先对数组排序,然后检查每个位置上的数字是否与索引相等。
代码实现
class Solution {
public int missingNumber(int[] nums) {
Arrays.sort(nums);
int n = nums.length;
for (int i = 0; i < n; i++) {
if (nums[i] != i) {
return i;
}
}
return n;
}
}
更优解法:数学求和法
class Solution {
public int missingNumber(int[] nums) {
int n = nums.length;
int expectedSum = n * (n + 1) / 2;
int actualSum = 0;
for (int num : nums) {
actualSum += num;
}
return expectedSum - actualSum;
}
}
算法对比
|
方法 |
时间复杂度 |
空间复杂度 |
特点 |
|
排序法 |
O(n log n) |
O(1) |
思路简单直接 |
|
数学法 |
O(n) |
O(1) |
效率最高,推荐使用 |
|
哈希法 |
O(n) |
O(n) |
需要额外空间 |
3. 岛屿的周长
问题描述
- 格子水平和垂直相连(对角线不相连)
- 恰好有一个岛屿
- 岛屿内部没有"湖"
输入:grid = [[0,1,0,0],[1,1,1,0],[0,1,0,0],[1,1,0,0]]输出:16解释:周长是16个黄色的边
输入:grid = [[1]]输出:4
输入:grid = [[1,0]]输出:4
解题思路:邻接检查法
代码实现
class Solution {
public int islandPerimeter(int[][] grid) {
int res = 0;
int n = grid.length, m = grid[0].length;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == 1) {
int add = 4; // 方格初始周长
// 检查四个方向的相邻格子
if (i - 1 >= 0 && grid[i - 1][j] == 1) add--; // 上
if (i + 1 < n && grid[i + 1][j] == 1) add--; // 下
if (j - 1 >= 0 && grid[i][j - 1] == 1) add--; // 左
if (j + 1 < m && grid[i][j + 1] == 1) add--; // 右
res += add;
}
}
}
return res;
}
}
算法分析
- 时间复杂度:O(n × m),需要遍历整个网格
- 空间复杂度:O(1),只使用常数空间
- 边界处理:注意检查数组边界,避免越界访问
可视化理解

4. 有序矩阵中第K小的元素
问题描述
给定一个 n × n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
示例说明:
示例 1:
输入:matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8
输出:13
解释:矩阵元素为 [1,5,9,10,11,12,13,13,15],第8小是13
示例 2:
输入:matrix = [[-5]], k = 1
输出:-5
解题思路:扁平化排序法
将二维矩阵转换为一维数组,排序后直接取第k小的元素。
代码实现
class Solution {
public int kthSmallest(int[][] matrix, int k) {
int n = matrix.length, m = matrix[0].length;
int[] nums = new int[n * m];
int index = 0;
for (int[] row : matrix) {
for (int num : row) {
nums[index] = num;
index++;
}
}
Arrays.sort(nums);
return nums[k - 1];
}
}
更优解法:二分搜索法
class Solution {
public int kthSmallest(int[][] matrix, int k) {
int n = matrix.length;
int left = matrix[0][0];
int right = matrix[n - 1][n - 1];
while (left < right) {
int mid = left + (right - left) / 2;
int count = countLessEqual(matrix, mid);
if (count < k) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
private int countLessEqual(int[][] matrix, int target) {
int count = 0;
int n = matrix.length;
int i = n - 1, j = 0;
while (i >= 0 && j < n) {
if (matrix[i][j] <= target) {
count += i + 1;
j++;
} else {
i--;
}
}
return count;
}
}
算法对比
|
方法 |
时间复杂度 |
空间复杂度 |
适用场景 |
|
扁平化排序 |
O(n² log n) |
O(n²) |
简单直接,小规模数据 |
|
二分搜索 |
O(n log(max-min)) |
O(1) |
大规模数据,推荐使用 |
|
堆方法 |
O(k log n) |
O(n) |
k较小时效率高 |
总结与技巧提炼
1. 双指针技巧
- 快指针负责遍历和条件判断
- 慢指针负责维护结果位置这种模式在数组操作中非常常见。
2. 数学优化思维
- 利用等差数列求和公式
- 避免不必要的排序操作
- 达到最优时间复杂度
3. 网格问题处理
- 遍历所有格子
- 检查四个方向
- 注意边界条件处理
4. 矩阵搜索策略
- 简单解法:扁平化后排序
- 优化解法:利用有序特性进行二分搜索
- 选择策略取决于数据规模和性能要求
实战建议
- 理解问题本质:每个问题都有其核心难点,找到关键点
- 多种解法对比:掌握不同时间/空间复杂度的解法
- 边界情况考虑:特别注意数组越界、空输入等情况
- 代码可读性:在保证效率的同时,保持代码清晰易懂
860

被折叠的 条评论
为什么被折叠?



