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

浙公网安备 33010602011771号