摆动排序 · 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/

1
2
3
4
5
6
7
8
9
10
11
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

 

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

 

1
2
3
4
5
6
7
8
9
10
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 差不多就是中位数!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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连续放到一起了!!!

好了,完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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

 

1
<br><br><br>
posted @   bonelee  阅读(84)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2022-02-05 烂土豆提权——本质上是利用SMB中间人漏洞利用,伪造一个token实现提权
2022-02-05 61:权限提升-Redis&Postgre&令牌窃取&进程注入;令牌窃取相当于窃取session-token;进程注入提权就是利用system权限的进程“附身”
2022-02-05 redis提权——如果对方root运行,则容易中招,都是由于安全配置出问题导致
2022-02-05 oracle提权执行命令工具oracleShell v0.1
2022-02-05 MSSQL提权之xp_cmdshell、SP_OACreate、沙盒提权
2022-02-05 MYSQL提权之反弹SHELL——数据库提权属于webshell到管理员的纵向提权,本质还是利用udf提权,无非是在mysql自定义函数中使用了反弹shell而已
2022-02-05 MySQL提权之启动项提权——开机启动的程序,那时候启动的程序权限都是system
点击右上角即可分享
微信分享提示