数组旋转是算法中的经典问题:给定一个数组,将数组元素向右(或向左)旋转k个位置。例如,数组
[1,2,3,4,5,6,7]向右旋转3位后变为 [5,6,7,1,2,3,4]。最直观的解法是使用额外数组存储旋转后的结果,但这需要O(n)的额外空间。本文将深入探讨三种空间复杂度O(1)的原地算法,让你在不使用额外数组的情况下优雅地解决这个问题。
方法一:环状替换法
这是最巧妙的O(1)空间解法,通过数学计算直接确定每个元素的最终位置。
算法思路:
-
每个元素都会移动到
(当前位置 + k) % n的位置 -
但直接移动会覆盖目标位置的元素,所以需要保存被覆盖的元素
-
从起始位置开始,沿着替换链一直移动,直到回到起始位置
-
如果还有未移动的元素,从下一个位置开始新的循环
时间复杂度:O(n) – 每个元素只移动一次
空间复杂度:O(1) – 只使用了几个临时变量
方法二:三次反转法
这种方法通过三次反转操作实现旋转,代码简洁且易于理解。
算法原理:
以数组
[1,2,3,4,5,6,7],k=3为例:-
整体反转:
[7,6,5,4,3,2,1] -
反转前3个:
[5,6,7,4,3,2,1] -
反转后4个:
[5,6,7,1,2,3,4]
时间复杂度:O(n) – 每个元素被访问两次
空间复杂度:O(1) – 原地操作
方法三:块交换法(递归实现)
这种方法将数组分成两块,通过递归交换实现旋转。
性能对比与选择建议
|
方法
|
时间复杂度
|
空间复杂度
|
优点
|
缺点
|
|---|---|---|---|---|
|
环状替换
|
O(n)
|
O(1)
|
每个元素只移动一次
|
逻辑较复杂
|
|
三次反转
|
O(n)
|
O(1)
|
实现简单,容易理解
|
每个元素访问两次
|
|
块交换
|
O(n)
|
O(1)
|
递归思路清晰
|
递归有栈空间开销
|
选择建议:
-
面试或竞赛:推荐三次反转法,代码简洁,容易讲解
-
生产环境:根据具体场景选择,三次反转法通常足够
-
学习理解:建议都实现一遍,理解不同思路
测试示例
总结
数组旋转问题虽然简单,但其中蕴含的算法思想却很深刻。三种O(1)空间复杂度的解法各有特点:
-
环状替换法展示了如何通过数学计算优雅处理循环依赖
-
三次反转法体现了”分而治之”的思想,将复杂问题分解为简单操作
-
块交换法展示了递归思维在数组操作中的应用
理解这些解法不仅能帮助你在面试中脱颖而出,更能提升你解决实际问题的算法思维。记住,优秀的算法不仅仅是正确的,更应该是高效且优雅的。