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