LeetCode 922简单双指针 · 滑窗
按奇偶排序数组 II 图解题解
这道题到底在问什么
给定整数数组 nums,已知一半是偶数、一半是奇数。重排后要求:nums[i] 为偶数时 i 也是偶数,nums[i] 为奇数时 i 也是奇数。返回任意一个满足条件的排列。
- 输入
- nums=[4,2,5,7]
- 输出
- [4,5,2,7] (偶位放偶数、奇位放奇数)
- 输入
- nums=[2,3]
- 输出
- [2,3] (本来就合规,原样返回)
最优解:一步一步想明白
- 3记住这套「偶位放偶数、奇位放奇数,i 和 j 各跳 2」,下面每一帧都在套它。
- 4先看规则:下标是偶数的格子要放偶数,下标是奇数的格子要放奇数。现在这个数组还没摆好。
- 5放两个指针:偶位指针标 i 从 0 起,奇位指针标 r(就是代码里的 j)从 1 起。i 只落偶数下标、j 只落奇数下标,各跳 2 步。
- 6红色这 4 个都放错了:偶位 0 和 2 上是奇数 3、5,奇位 1 和 5 上是偶数 4、2。它们正好两两配对,一次交换能同时修好两处。
- 7i 跳到偶位 0,看看上面是什么:这里是 3。
- 83 是奇数,却站在偶位 0,放错了(变红)。要去奇位那边找一个放错的偶数来跟它换。
- 9j 到奇位 1,这里是 4,偶数却站在奇位,正是放错的,就用它跟偶位 0 交换。
- 10交换:偶位 0 拿到偶数 4,奇位 1 拿到奇数 3,两边同时归位(变绿)。
- 11i 跳到偶位 2,看看上面是什么:这里是 5。
- 125 是奇数,却站在偶位 2,放错了(变红)。要去奇位那边找一个放错的偶数来跟它换。
- 13j 在奇位 1,这里是 3,奇数待在奇位是对的,不能动,j 加 2 接着往后找。
- 14j 在奇位 3,这里是 7,奇数待在奇位是对的,不能动,j 加 2 接着往后找。
- 15j 到奇位 5,这里是 2,偶数却站在奇位,正是放错的,就用它跟偶位 2 交换。
- 16交换:偶位 2 拿到偶数 2,奇位 5 拿到奇数 5,两边同时归位(变绿)。
- 17i 跳到偶位 4,看看上面是什么:这里是 6。
- 186 是偶数,正好站在偶位 4,本来就对(变绿),跳过,i 接着往后走。
- 19i 跳到偶位 6,看看上面是什么:这里是 8。
- 208 是偶数,正好站在偶位 6,本来就对(变绿),跳过,i 接着往后走。
- 21i 把偶位 0、2、4、6 都看过了,循环结束。下面逐位验收一下结果。
- 22先看偶位:0、2、4、6 上分别是 4、2、6、8,全是偶数,符合要求。
- 23再看奇位:1、3、5、7 上分别是 3、7、5、1,全是奇数,也符合要求。
- 24整个数组摆好了:偶位放偶数、奇位放奇数。答案就是 [4,3,2,7,6,5,8,1]。
⚠️ 容易写错的地方
✗ 错:i、j 都只 +1 逐个走
✓ 对:i、j 各 +2,只落在各自那半的下标
偶位指针只能停偶下标、奇位指针只能停奇下标,跳 2 才对得上奇偶
✗ 错:每轮让 j 都从 1 重新扫
✓ 对:j 不重置,跨轮接着往后走
重置会退化成 O(n²),还会重复检查已经对位的元素
✗ 错:偶位放错时去找另一个奇数换
✓ 对:要找奇位上放错的偶数
偶位的奇数 ⇄ 奇位的偶数,一次交换两处同时归位
完整代码(Python / C++ / Java)
Python
from __future__ import annotations
from typing import *
from collections import *
from functools import *
from itertools import *
from math import *
from heapq import *
from bisect import *
class Solution:
def sortArrayByParityII(self, nums: List[int]) -> List[int]:
n, j = len(nums), 1
for i in range(0, n, 2):
if nums[i] % 2:
while nums[j] % 2:
j += 2
nums[i], nums[j] = nums[j], nums[i]
return numsC++
#include <algorithm>
#include <array>
#include <cctype>
#include <climits>
#include <cmath>
#include <deque>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
class Solution {
public:
vector<int> sortArrayByParityII(vector<int>& nums) {
for (int i = 0, j = 1; i < nums.size(); i += 2) {
if (nums[i] % 2) {
while (nums[j] % 2) {
j += 2;
}
swap(nums[i], nums[j]);
}
}
return nums;
}
};Java
import java.util.*;
class Solution {
public int[] sortArrayByParityII(int[] nums) {
for (int i = 0, j = 1; i < nums.length; i += 2) {
if (nums[i] % 2 == 1) {
while (nums[j] % 2 == 1) {
j += 2;
}
int t = nums[i];
nums[i] = nums[j];
nums[j] = t;
}
}
return nums;
}
}复杂度
时间
O(n)
i 和 j 各自只向前走一遍,合计扫一遍数组
空间
O(1)
原地交换,只用 i、j 两个下标变量
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 按奇偶排序数组 II 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
为什么这样交换一定不会破坏已经摆好的位置?+
i 只增不减地走偶位,j 也只增不减地走奇位,两者都不回头。每次被换走的都是「放错的」元素,换过来的正好补上对方的缺;已经确认对位的元素,j 不会再走回去碰它,所以不会被破坏。
进阶要求不用额外空间,这个解满足吗?+
满足。全程在原数组上原地交换,只用了 i、j 两个下标,空间 O(1)。另一种写法是开一个新数组、用两个写指针分别往偶位奇位填,思路更直观但要 O(n) 额外空间;本解更省。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 按奇偶排序数组 II 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。