摆动排序 · Wiggle Sort 算法编程-数学归纳法和quick select的使用

280. 摆动排序

给你一个的整数数组 nums, 将该数组重新排序后使 nums[0] <= nums[1] >= nums[2] <= nums[3]... 

输入数组总是有一个有效的答案。

 

示例 1:

输入:nums = [3,5,2,1,6,4]
输出:[3,5,1,6,2,4]
解释:[1,6,2,5,3,4]也是有效的答案

示例 2:

输入:nums = [6,6,5,6,3,8]
输出:[6,6,5,6,3,8]

 

提示:

 

  • 1 <= nums.length <= 5 * 104
  • 0 <= nums[i] <= 104
  • 输入的 nums 保证至少有一个答案。

 

进阶:你能在 O(n) 时间复杂度下解决这个问题吗?

 

-----

一个显而易见的解法是先将数组排序,再从第二个元素开始逐对交换元素的位置。如:

   [1, 2, 3, 4, 5, 6]
       ↑  ↑  ↑  ↑
       swap  swap

=> [1, 3, 2, 5, 4, 6]

链接:https://leetcode.cn/problems/wiggle-sort/solution/bai-dong-pai-xu-by-leetcode/

class Solution:
    def wiggleSort(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        nums.sort()
        n = len(nums)        
        j = 1
        while j+1 < n and j < n:
            nums[j], nums[j+1] = nums[j+1], nums[j]
            j += 2

 

也可以不排序,直接交换:

 

class Solution:
    def wiggleSort(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n = len(nums)        
        for i in range(1, n):
            if (i % 2 == 0 and nums[i] > nums[i-1]) or \
               (i % 2 == 1 and nums[i] < nums[i-1]):  
                nums[i], nums[i-1] = nums[i-1], nums[i]            

 证明下,用数学归纳法:

(1)若i是偶数,则说明:nums[i-2],nums[i-1]是降序。

nums[i-1]和nums[i]的关系分为两种情况:

(1.a)nums[i-1] <= nums[i],这种情况,不需要做任何交换!因为:nums[i-2] >= nums[i-1] <= nums[i]

(1.b)nums[i-1] > nums[i],这种情况,需要交换!变为:nums[i-2] >=nums[i] > nums[i-1]则满足答案!可以用反证法证明:如果交换以后不满足答案,则说明:nums[i-2] < nums[i], 由于nums[i-2] >= nums[i-1],所以有nums[i-1]<=nums[i-2] < nums[i] ==> nums[i-1] < nums[i] ,这和最初的条件nums[i-1] > nums[i]矛盾!!!

画个图更直观:这种情况直接交换下nums[i-1] , nums[i]就好啦!

nums[i-2]

      \

      nums[i-1]

           \

          nums[i]

(2)若i是奇数,则说明:nums[i-2],nums[i-1]是升序。

(2.a)nums[i-1] >= nums[i],这种情况,不需要做任何交换!因为:nums[i-2] <= nums[i-1] >= nums[i]

(2.b)nums[i-1] < nums[i],这种情况,需要交换!变为:nums[i-2] <= nums[i] > nums[i-1]则满足答案!同样可以用反证法证明!

画个图: 这种情况直接交换下nums[i-1] , nums[i]就好啦!

        nums[i]

         /

     nums[i-1]

      /

nums[i-2]

 

324. 摆动排序 II

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

你可以假设所有输入数组都可以得到满足题目要求的结果。

 

示例 1:

输入:nums = [1,5,1,1,6,4]
输出:[1,6,1,5,1,4]
解释:[1,4,1,5,1,6] 同样是符合题目要求的结果,可以被判题程序接受。

示例 2:

输入:nums = [1,3,2,2,3,1]
输出:[2,3,1,3,1,2]

 

提示:

  • 1 <= nums.length <= 5 * 104
  • 0 <= nums[i] <= 5000
  • 题目数据保证,对于给定的输入 nums ,总能产生满足题目要求的结果

 

进阶:你能用 O(n) 时间复杂度和 / 或原地 O(1) 额外空间来实现吗?

 

这个题目,就不能用上面的思路了!!!

 

 left 差不多就是中位数!

class Solution {
public void wiggleSort(int[] nums) {
        int[] clone = nums.clone();
        Arrays.sort(clone);
        //两个指针
        int left = (nums.length - 1) / 2, right = nums.length - 1;
        for (int i = 0; i < nums.length; i++) {
            if (i % 2 == 0) {
                nums[i] = clone[left];
                left--;
            } else {
                nums[i] = clone[right];
                right--;
            }
        }
    }
}

 

好了有了上面的铺垫,就可以使用quick select先找到中位数,再来交换了。

 

注意下面的:再将所有等于target都集中放到一起?为啥要做这步呢!!!
我们举例看看上面代码效果:
nums= [1, 2, 2, 5, 2, 1, 5, 2, 1, 3, 4, 4, 1, 5, 2]
1==> [1, 1, 1, 1, 2, 2, 2, 2, 3, 4, 4, 2, 5, 5, 5]
2==> [1, 1, 1, 1, 2, 2, 2, 2, 2, 4, 4, 3, 5, 5, 5]
是不是2放到后面某一个地方去了。。。。
去掉代码,然后排序结果:
[2, 5, 2, 5, 2, 5, 2, 2, 1, 4, 1, 4, 1, 3, 1]
两个2连续放到一起了!!!

好了,完整代码:

def quick_select(arr, l, r, index):
    q = partition(arr, l, r)
    if q == index:
        return arr[q]
    if q < index:
        return quick_select(arr, q + 1, r, index)
    return quick_select(arr, l, q - 1, index)


def partition(nums, l, r):
    pivot = nums[l]
    index = l+1
    for i in range(l+1, r+1):
        if nums[i] < pivot:
            nums[i], nums[index] = nums[index], nums[i]
            index += 1
    index -= 1
    nums[index], nums[l] = nums[l], nums[index]
    return index


class Solution:
    def wiggleSort(self, nums):
        n = len(nums)
        x = (n + 1) // 2
        # 先找到中位数
        target = quick_select(nums, 0, n - 1, x - 1)

        # print("1==>", nums)
        # 再将所有等于target都集中放到一起
        index = x
        for i in range(x+1, n):
            if nums[i] == target:
                nums[index], nums[i] = nums[i], nums[index]
                index += 1

        # print("2==>", nums)
        arr = nums.copy()
        left = (n - 1)//2
        right = n - 1
        for i in range(n):
            if i % 2 == 0:
                nums[i] = arr[left]
                left -= 1
            else:
                nums[i] = arr[right]
                right -= 1

 




posted @ 2023-02-05 12:31  bonelee  阅读(56)  评论(0编辑  收藏  举报