双指针典型排序题
题目
一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
1.最简单的思路:暴力排序
- 遍历数组,计算每个数的平方。
- 将平方值存入新数组。
- 对新数组使用排序算法(如
Arrays.sort()
)。
class Solution {
public int[] sortedSquares(int[] nums) {
int[] ans = new int[nums.length];
for (int i = 0; i < nums.length; ++i) {
ans[i] = nums[i] * nums[i];
}
Arrays.sort(ans);
return ans;
}
}
- 时间复杂度:O(nlogn)
- 遍历数组计算平方的复杂度为 O(n)。
- 排序复杂度为 O(nlogn) (Arrays.sort()底层结合了多种排序算法,对于没有特点的数据来说,在排序算法中性能很好)。
- 空间复杂度:O(n),需要额外的数组存储平方值。
2.双指针
-
特性分析:输入数组为非递减顺序(单调递增),负数的平方可能比正数大,因此平方值的分布呈 "两头大中间小" 的趋势。
-
解决方法:找到数组中负数和非负数的分界点。使用两个指针分别从分界点的两侧开始比较,逐步将较小的平方值加入结果数组。
class Solution {
public int[] sortedSquares(int[] nums) {
int n = nums.length;
int negative = -1;
// 找到负数和非负数的分界点
for (int i = 0; i < n; ++i) {
if (nums[i] < 0) {
negative = i;
} else {
break;
}
}
int[] ans = new int[n];
int index = 0, i = negative, j = negative + 1;
// 合并负数平方和非负数平方
while (i >= 0 || j < n) {
if (i < 0) {
ans[index] = nums[j] * nums[j];
++j;
} else if (j == n) {
ans[index] = nums[i] * nums[i];
--i;
} else if (nums[i] * nums[i] < nums[j] * nums[j]) {
ans[index] = nums[i] * nums[i];
--i;
} else {
ans[index] = nums[j] * nums[j];
++j;
}
++index;
}
return ans;
}
}
- 时间复杂度:O(n)
- 数组遍历寻找分界点为 O(n)(这里其实可以使用二分查找优化为O(logn))。
- 双指针合并过程为 O(n)。
- 空间复杂度:O(n),需要额外的数组存储结果。
扩展题目
- 移动零 (
Move Zeroes
):将数组中的所有零移动到末尾,同时保持非零元素的相对顺序。 - 合并两个有序数组:使用双指针实现。
- 最大滑动窗口:扩展为动态窗口问题。