目录
题目解析
题目要求将非负整数数组重新排列,使得所有偶数元素位于偶数索引(0, 2, 4, ...),所有奇数元素位于奇数索引(1, 3, 5, ...)。题目保证至少存在一个有效解,且答案不唯一。
示例输入输出:
-
输入:
nums = [4,2,5,7]
-
输出:
[4,5,2,7]
(或其他合法排列)
算法思路:双指针原地交换
核心思想是通过双指针分别跟踪偶数索引和奇数索引,逐步将元素调整到正确的位置。
关键步骤:
-
初始化双指针:
-
even
指针从0
开始,步长为2
,用于寻找偶数索引处的奇数元素。 -
odd
指针从1
开始,步长为2
,用于寻找奇数索引处的偶数元素。
-
-
循环处理:
-
若
even
指向的元素是偶数,说明当前位置正确,even
后移2
步。 -
若
odd
指向的元素是奇数,说明当前位置正确,odd
后移2
步。 -
否则,交换
even
和odd
指向的元素,这样两个位置的元素均满足条件。
-
-
终止条件:
-
当任意指针超出数组长度时,所有元素已正确排列。
-
正确性证明:
每次交换操作至少修复两个位置的错误(一个偶数的位置和一个奇数的位置),由于题目保证输入有解,算法最终会将所有元素归位。
/**
* @param {number[]} nums
* @return {number[]}
*/
var sortArrayByParityII = function(nums) {
let even = 0; // 偶数索引
let odd = 1; // 奇数索引
while (even < nums.length && odd < nums.length) {
if (nums[even] % 2 === 0) { // 如果偶数索引处的数字是偶数
even += 2; // 移动到下一个偶数索引
} else if (nums[odd] % 2 === 1) { // 如果奇数索引处的数字是奇数
odd += 2; // 移动到下一个奇数索引
} else { // 如果偶数索引处的数字是奇数且奇数索引处的数字是偶数
[nums[even], nums[odd]] = [nums[odd], nums[even]]; // 交换它们
}
}
return nums; // 返回排序后的数组
};
console.log(sortArrayByParityII([4, 2, 5, 7]));
示例详解
以输入 nums = [4,2,5,7]
为例:
-
初始状态:
-
even = 0
,odd = 1
。 -
检查
nums[0] = 4
(偶数),even
移动到2
。
-
-
第二轮循环:
-
nums[2] = 5
(奇数),检查odd = 1
处的nums[1] = 2
(偶数)。 -
交换两者,数组变为
[4,5,2,7]
。
-
-
指针更新:
-
even
移动到4
,超出数组长度,循环终止。
-
最终结果为 [4,5,2,7]
,所有偶数位于偶数索引,奇数位于奇数索引。
复杂度分析
-
时间复杂度:O(n),每个元素最多被访问一次。
-
空间复杂度:O(1),原地交换,仅使用常数空间。
对比其他解法
-
额外空间法:
-
分别收集偶数和奇数,依次填充到偶数索引和奇数索引。
-
时间复杂度 O(n),但空间复杂度 O(n)。
-
-
两次遍历法:
-
第一次处理偶数,第二次处理奇数,但实现较为繁琐。
-
当前的双指针法在时间和空间上均最优,适合大数据量的场景。
总结
通过双指针一次遍历,原地交换元素,该算法高效且优雅地解决了奇偶排序问题。理解双指针移动的条件和交换的动机是掌握此算法的关键。