leetcode摆动排序

这篇博客探讨了一种特殊的数组排列问题,即如何将一个有序数组以交错方式重新排列。作者首先提出了一个直观但不完全正确的解决方案,然后通过逆序插入和分类讨论的方法解决了这个问题。在偶数长度的数组中,通过比较中间两个元素并逆序插入,确保了交错效果。同时,博客还提到了桶排序作为进一步优化的可能性,以实现线性时间复杂度的解法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题干信息

在这里插入图片描述

  • 这道题很容易想到利用排序来实现
  • 将nums数组前后半部分分开,将后半部分插入前半部分两两之间即可。直观上感觉这样做是对的。实际上有问题!
    • 考虑当nums为[4,5,5,2]时,按上面的做法得出的结果和nums原本是一致的。而实际上nums可以按照题意变为[4,5,2,5]

IDEA1:考虑逆序插入+分类讨论

当nums.length为偶数时,考虑将Nums首先从小到大进行排序,再将nums分成大小相同的两部分。设nums.length = n,则将nums分为的第一部分为
n u m s [ 0 ] , n u m s [ 1 ] , ⋯ n u m s [ n / 2 − 1 ] nums[0],nums[1],\cdots nums[n/2 -1] nums[0],nums[1],nums[n/21]
将nums分为的第二部分为: n u m s [ n / 2 ] , n u m s [ n / 2 + 1 ] , ⋯ n u m s [ n − 1 ] nums[n/2],nums[n/2+1],\cdots nums[n-1] nums[n/2],nums[n/2+1],nums[n1]
容易看出两者的长度是相同的。

  • 如果 n u m s [ n / 2 ] > n u m s [ n / 2 − 1 ] nums[n/2]>nums[n/2-1] nums[n/2]>nums[n/21]时,我们可以放心的将后一个序列插入前一个序列之间,亦即最终构成序列 n u m s [ 0 ] , n u m s [ n / 2 ] , n u m s [ 1 ] , n u m s [ n / 2 + 1 ] ⋯ n u m s [ n − 1 ] , n u m s [ n / 2 − 1 ] nums[0],nums[n/2],nums[1],nums[n/2+1]\cdots nums[n-1],nums[n/2-1] nums[0],nums[n/2],nums[1],nums[n/2+1]nums[n1],nums[n/21]
  • 但不能排除: n u m s [ n / 2 ] = n u m s [ n / 2 − 1 ] nums[n/2]=nums[n/2-1] nums[n/2]=nums[n/21]的情况!
    • 如果按照上述思路的话 [ 4 , 5 , 5 , 2 ] [4,5,5,2] [4,5,5,2]的例子就不行!
    • 逆序插入可以很好的解决这一问题。将前一个序列逆序,后一个序列逆序。即最终构成的结果为
      n u m s [ n / 2 − 1 ] , n u m s [ n − 1 ] , n u m s [ n / 2 − 2 ] , n u m s [ n − 2 ] , ⋯ n u m s [ 0 ] , n u m s [ n / 2 ] nums[n/2-1],nums[n-1],nums[n/2-2],nums[n-2],\cdots nums[0],nums[n/2] nums[n/21],nums[n1],nums[n/22],nums[n2],nums[0],nums[n/2]

下面证明为什么上述逆序插入的思路是可行的。显然当 n u m s [ n / 2 ] > n u m s [ n / 2 − 1 ] nums[n/2]>nums[n/2-1] nums[n/2]>nums[n/21]时逆序插入的思路是可行的。只证明当 n u m s [ n / 2 ] = n u m s [ n / 2 − 1 ] nums[n/2]=nums[n/2-1] nums[n/2]=nums[n/21]上述思路是可行的。

  • 首先,我们一定有 n u m s [ n / 2 − 1 ] < n u m s [ n − 1 ] nums[n/2-1]<nums[n-1] nums[n/21]<nums[n1].若不然则 n u m s [ n / 2 − 1 ] = n u m s [ n − 1 ] nums[n/2-1]=nums[n-1] nums[n/21]=nums[n1],则由于nums是经过排序的,至少一定有 n u m s [ n − 1 ] = n u m s [ n − 2 ] = ⋯ = n u m s [ n / 2 − 1 ] nums[n-1] = nums[n-2] = \cdots = nums[n/2 - 1] nums[n1]=nums[n2]==nums[n/21].这其中的数一共有: n − n / 2 + 1 = n / 2 + 1 n-n/2+1 = n/2+1 nn/2+1=n/2+1个数,即相等的数比nums总长度的一半还多。因此无论如何怎样排列总有两个相同的数连在一起,不可能实现本题目所示的结果,这和题目的前提矛盾!
  • 类似可以证明 n u m s [ n / 2 − 2 ] < n u m s [ n − 2 ] nums[n/2-2]<nums[n-2] nums[n/22]<nums[n2]一直到 n u m s [ 0 ] < n u m s [ n / 2 ] nums[0] < nums[n/2] nums[0]<nums[n/2]

奇数情况可以类似证明。
下面的AC代码将奇数情况和偶数情况合并了。是高级语言中整除运算符的特性(向下取整)。

    public void wiggleSort(int[] nums) {
        int[] temp = nums.clone();
        Arrays.sort(temp);
        for(int i = 0;i<nums.length;i+=2)
        {	//这里的(nums.length - 1)/2实际上是将两种情况都合并了!!
            nums[i] = temp[(nums.length - 1)/2 - i/2];
        }
        for(int i = 1;i<nums.length;i+=2)
        {
            nums[i] = temp[nums.length - 1 - i / 2];
        }       
    }

IDEA2:优化1-桶排序

根据题目所示的条件:0 <= nums[i] <= 5000。可以考虑使用桶排序,复杂度为 O ( n ) O(n) O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值