摆动排序

把数组排成“小-大-小-大”的样子。

Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3]...

按逆序按中位数划分两半;把[N/2..](后面小的一半或一半多1)放在偶位,再把[0..N/2)(前面大的一半)放在奇位。

比如 1 2 3 3 3 3 4 5,逆序 5 4 3 3 3 3 2 1
先把 3 3 2 1 逆序放在偶位:3 · 3 · 2 · 1
再把 5 4 3 3 逆序放在奇位:· 5 · 4 · 3 · 3
变成:3 5 3 4 2 3 1 3

这种把后一半坐标[N/2..]放到偶位、[0..N/2)放到奇位的下标映射是:idx => (idx*2+1)%(N|1)

比如 0 1 2 3 4 5 6 7
逆序 7 6 5 4 3 2 1 0
变成 3 7 2 6 1 5 0 4
下标 6 4 2 0 7 5 3 1

若把数组排成“大-小-大-小”的样子。

按中位数划分两半;把[N/2..](后面大的一半或一半多1)放在偶位,再把[0..N/2)(前面小的一半)放在奇位。

比如 1 2 3 3 3 3 4 5
先把 3 3 4 5 放在偶位:3 · 3 · 4 · 5
再把 1 2 3 3 放在奇位:· 1 · 2 · 3 · 3
变成:3 1 3 2 4 3 5 3

可以把按中位数划分、按奇偶位摆放两个过程整合,在“三路划分”的过程中用虚拟索引摆放到位,这样只要O(1)空间。

void wiggleSort(vector<int>& nums) {
    const int N = nums.size();
    // 按逆序处理,下面先找media、再按>median三路划分
    // 可自己实现findKthLargest,其中k是1-based,中位数k=(N+1)/2
    // int median = findKthLargest(nums, (N + 1) / 2);
    // 见 https://leetcode.com/problems/kth-largest-element-in-an-array/
    // 这里也可以调用nth_element(..., greater<int>()),其中midptr是0-based
    auto midptr = nums.begin() + (N - 1) / 2; // 0-based偏移中位数(N+1)/2-1
    nth_element(nums.begin(), midptr, nums.end(), greater<int>());
    int median = *midptr;
    
    // 把后一半坐标[N/2..]放到偶位、前一半坐标[0..N/2)放到奇位的下标映射:i => (2*i+1) % (N|1)
    // 见 https://leetcode.com/problems/wiggle-sort-ii/discuss/77677/O(n)+O(1)-after-median-Virtual-Indexing
    auto idx = [&](int i) { return (2*i+1) % (N|1); };
    // 三路划分:| >median | =median |  ?  | <median |
    //          0         i         j    k        N-1
    // i指向>median待写入位置,j指向待处理位置,k指向<median待写入位置
    int i = 0, j = 0, k = N - 1;
    while (j <= k) {
        if (nums[idx(j)] > median) {
            swap(nums[idx(j)], nums[idx(i)]);
            i++, j++;
        } else if (nums[idx(j)] < median) {
            swap(nums[idx(j)], nums[idx(k)]);
            k--;
        } else {
            j++;
        }
    }
}

Given an unsorted array nums, reorder it in-place such that nums[0] <= nums[1] >= nums[2] <= nums[3]....

条件更宽松,<=、>=,只要把不符合要求的相邻数对换

void wiggleSort(vector<int>& nums) {
    for (int i = 0; i + 1 < nums.size(); i++) {
        if ((i % 2 == 0 && nums[i] > nums[i+1]) || (i % 2 == 1 && nums[i] < nums[i+1])) {
            swap(nums[i], nums[i+1]);
        }
    }
}

最长摆动子序列、最长摆动子段只需用贪心法