摘要
在这篇文章中,我们将一起拆解 LeetCode 第 280 题《摆动排序》,并用 Swift 编写一个简单又高效的解法。我们会从题目描述开始,逐步分析解题思路,提供代码实现,并解释其中的逻辑。最后还会结合实际场景,谈谈这个“看似无用”的题目,其实在某些交互排序中非常有用。
描述
题目来自 LeetCode 280:Wiggle Sort(摆动排序)。
给你一个整数数组 nums
,请重新排列它们,使得:
nums[0] <= nums[1] >= nums[2] <= nums[3]...
依此类推。
简单点说,就是让数组在数值上形成一种“波浪”型的排序。高低交错,看起来像这样:
输入: [3, 5, 2, 1, 6, 4]
输出: [3, 5, 1, 6, 2, 4]
注意:
-
可以有多个合法解,题目只要求 满足条件即可,并不要求特定顺序。
-
要求 就地排序(in-place),不额外开辟空间。
题解答案
直觉上我们会想用排序再调换位置的办法,但其实我们完全可以用一轮遍历就搞定这个问题,而且效率非常高。
基本思路:
-
遍历数组的每一对相邻元素。
-
在奇数索引位,确保
nums[i] >= nums[i - 1]
。 -
在偶数索引位,确保
nums[i] <= nums[i - 1]
。 -
如果不符合,就交换它们。
题解代码分析
func wiggleSort(_ nums: inout [Int]) {
for i in 1..<nums.count {
if (i % 2 == 1 && nums[i] < nums[i - 1]) ||
(i % 2 == 0 && nums[i] > nums[i - 1]) {
nums.swapAt(i, i - 1)
}
}
}
代码逐行解析:
-
for i in 1..<nums.count
:从第 1 个元素开始遍历,因为我们需要和前一个元素进行比较。 -
i % 2 == 1
:如果当前是奇数索引位,应该是高峰,nums[i] >= nums[i - 1]
。 -
i % 2 == 0
:偶数位是低谷,应该满足nums[i] <= nums[i - 1]
。 -
如果违反这些条件,我们用
swapAt()
交换这两个数。
这段代码只需要一次遍历,所以非常高效!
示例测试及结果
来看看几组实际测试结果:
var test1 = [3, 5, 2, 1, 6, 4]
wiggleSort(&test1)
print(test1) // 输出示例: [3, 5, 1, 6, 2, 4]
var test2 = [1, 2, 3, 4, 5, 6]
wiggleSort(&test2)
print(test2) // 输出示例: [1, 3, 2, 5, 4, 6]
var test3 = [6, 5, 4, 3, 2, 1]
wiggleSort(&test3)
print(test3) // 输出示例: [5, 6, 3, 4, 1, 2]
输出是否唯一并不重要,只要符合交错条件即可。
时间复杂度
- 时间复杂度:O(n)
只遍历了一次数组,没有多余的操作或嵌套循环。
空间复杂度
- 空间复杂度:O(1)
采用就地交换,不依赖额外空间,非常节省内存资源。
总结
这道题虽然简单,但确实考验了我们对条件的理解能力和代码实现的简洁度。在实际开发中,这类“局部有序”的场景常出现在数据可视化展示、图形控件排布等需要一定“节奏感”的排序中,摆动排序就是一种低成本高表现的方式。
实际场景类比
想象你在开发一个电商商品列表页,要展示用户喜好度的走势图——这个走势就不希望一条直线或者单调增减,而是要有节奏的“起伏”才更直观。
或者你在做一个推荐引擎排序优化,希望将热门和冷门内容交错展示以提升点击率,也可以用类似的摆动策略来打散排序。
未来展望
可以继续延伸这道题,比如:
-
如果数组中有重复值,会不会影响结果?
-
如果要求
nums[0] > nums[1] < nums[2] > nums[3]...
呢? -
如果我们希望构造最大的摆动幅度,应该怎么做?
这些都可以作为后续思考题,进一步挑战自己。