摆动排序

题目描述

给定一个无序的数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]... 的顺序。

示例 1:

输入: nums = [1, 5, 1, 1, 6, 4]
输出: 一个可能的答案是 [1, 4, 1, 5, 1, 6]

思路

首先该数组满足类似于小,大,小,大…类似的元素关系。那么假设对于一个有序的数组,我们可以考虑按一般划分,将左边部分填充到小的位置,将右边部分填充到大的位置。

但在这题上,我们并不需要关系每个小之间的关系,也就是说只要能将小和大分为两部分就可以了,不需要再排序小或大内部的元素。

因此考虑按快排思路,以中间的基准数进行调整,就可以将数组分为一半小和一半大。

除此之外要需要考虑到中间元素较多的情况,比如[1,2,3,3,3,4],分为两部分,[1,2,3]和[3,3,4],直接进行填位,就变成[1,3,2,3,3,4],不符合题目要求。因此需要将中间重复的元素尽可能分离,所以讲两个数组倒序,变成[3,2,1]和[4,3,3],填位变成[3,4,2,3,1,3]

code

func wiggleSort(nums []int) {
    k := (len(nums) + 1) / 2
    var l, r, lt, gt int
    l, r = 0, len(nums)
    lt, gt = tripleSort(nums, l, r)
    for true {
        if len(nums)-k >= lt && len(nums)-k < gt {
            break
        }
        if gt <= len(nums)-k {
            l = gt
            lt, gt = tripleSort(nums, l, r)
        } else {
            r = lt
            lt, gt = tripleSort(nums, l, r)
        }
    }
    ans, i := make([]int, len(nums)), 0
    for 2*i < len(nums) {
        ans[2*i] = nums[(len(nums)-1)/2-i]
        if 2*i+1 < len(nums) {
            ans[2*i+1] = nums[len(nums)-1-i]
        }
        i++
    }
    for k, v := range ans {
        nums[k] = v
    }
}
func tripleSort(nums []int, l, r int) (int, int) {
    if l >= r {
        return -1, -1
    }
    q, lt, i, gt := nums[l], l, l+1, r
    for i < gt {
        if nums[i] < q {
            nums[lt+1], nums[i] = nums[i], nums[lt+1]
            lt++
            i++
        } else if nums[i] > q {
            nums[gt-1], nums[i] = nums[i], nums[gt-1]
            gt--
        } else {
            i++
        }
    }
    nums[lt], nums[l] = nums[l], nums[lt]
    return lt, gt
}