题目描述
自己解法
解法一 - 暴力求解
直接原地移动数组,时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( 1 ) O(1) O(1):
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
length = len(nums)
k = k % length
for i in range(length-k,length):
for m in range(i-1,i-length+k-1,-1):
nums[m],nums[m+1] = nums[m+1],nums[m]
运行结果,超出时间限制:
输入数组的长度为2000,算法的复杂度为
O
(
n
2
)
O(n^2)
O(n2),超时在意料之中。
解法二 - 分治法
我们首先考虑什么情况下移动数组不会改变数组本身,有两种情况(分治法的临界条件):
- 数组长度为1,不管移动多少位,数组不变
- 数组长度与移动位数刚好一致,移动后数组不变
本题分治法的思想是不断交换划分子数组,直到达到临界条件,
假设输入数组为[1,2,3,4,5,6,7]
,移动位数k=3
,如下图所示:
由题意可知,是想让原数组向右移动3位
,第一步先把[5,6,7]
三个元素移动到最终位置上,即与[1,2,3]
逐个交换:
至此,虚线左侧
的数组已经完成移动,但是虚线右侧
的数组似乎已经乱了。关键的地方来了,通过观察得知,虚线右侧的数组[4,1,2,3]
其实是由[1,2,3,4]向左移动3位
(题目中k=3)得出的。
第一步将[5,6,7]
向右
移动3位,交换到最终位置上,此时虚线左侧
的数组不用考虑了。后果是虚线右侧
的子数组向左
移动了3位,为了抵消这一影响,需要将虚线右侧的子数组向右
平移3位,交换位置,也就是重复第一步的操作。切记,虚线左侧的数组已经可以不用管它了。结果为:
这时,算法来到了终止条件之一:虚线右侧的子数组长度为1
,不管对子数组怎么移动,子数组本身都不会变,算法结束,得到最终的数组就是[5,6,7,1,2,3,4]
下面演示第二种终止条件,输入数组为[1,2,3,4]
,k=2
:
第一步后,虚线右侧的子数组实际上向左平移了2位,但是由于子数组长度和平移位数一致,所以数组不变,到达终止条件。
代码如下:
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
length = len(nums)
k = k % length
childLength = len(nums) # 子数组长度
childStart = 0 #子数组起始下标
while True:
if childLength == 1 or k == 0: # 终止条件
break
temp = 0
for i in range(length-k,length):
nums[i],nums[childStart+temp] = nums[childStart+temp],nums[i]
temp += 1
childLength -= k
childStart += k
k %= childLength
算法复杂度为
O
(
n
)
O(n)
O(n),空间复杂度为
O
(
1
)
O(1)
O(1)。最坏的情况下是k=1
的情况,需要逐个交换元素,执行结果: