一.问题描述
Given an array, rotate the array to the right by k steps, where k is non-negative.
Example 1:
Input:[1,2,3,4,5,6,7]
and k = 3 Output:[5,6,7,1,2,3,4]
Explanation: rotate 1 steps to the right:[7,1,2,3,4,5,6]
rotate 2 steps to the right:[6,7,1,2,3,4,5]
rotate 3 steps to the right:[5,6,7,1,2,3,4]
Example 2:
Input: [-1,-100,3,99]
and k = 2
Output: [3,99,-1,-100]
Explanation:
rotate 1 steps to the right: [99,-1,-100,3]
rotate 2 steps to the right: [3,99,-1,-100]
Note:
- Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem.
- Could you do it in-place with O(1) extra space?
二.解题思路
这道题是我觉得出得非常好的一道题。
如果不要求O(1)内存,那非常简单,用python的切片很快搞定。
方法一:切片
移动k次,先k和n求余数,因为移动n+tk次和移动k次是一样的。
然后,用一个临时变量保存数组后面的k个数,因为移动后这k个数会变成数组最前面的k个数。
然后给nums赋值,具体可以看代码。
时间复杂度O(N),空间复杂度O(K)。
下面着重介绍两个O(1)内存的方法,特别是方法三。
方法二:反转
1.首先将整个列表反转,这时候最后k个数已经跑到了数字前面去了,但是数反着的,所以。
2.将前k个数再反转,次数数组前k个数处理完毕,正序,但是后面的数还是反着的。所以。
3.将数组k+1到结束的位置再反转,此时整个数组处理完毕。
时间复杂度O(N),空间复杂度O(1).
方法三:这个我也不知道咋命名
首先我们知道,把nums[i]往后移动k个位置,就是这个数移动后的正确位置,那么我们可以迭代的,让nums[i+k]=nums[i],(用一个变量保存原来的nums[i+k]),然后再让nums[i+2k]=temp(这个temp保存这原来nums[i+k]的值),然后用一个计数器计数我们总共赋值了多少,如果计数器到达n,说明数组中所有的位置都处理了一遍。
这个算法成立涉及到两个知识:
1.在一个数组中,我们从位置i开始,持续的以间隔k遍历数组,(如果超过数组长度n,则遍历位置为要遍历的位置余n),迟早会重新遍历到i。
证明:
假设数组长度和间隔k都是一个偶数。
数组长度 n = 2t , 间隔 k = 2j
开始位置为 i, 我们可以把超过数组长度则求余转换成,无数个数组拼起来(方便理解),问题转化成
有这样的x和y,满足xn+i==yk+i
即2tx+i==2jy+i ---> tx=jy, 很明显存在这样的x和y满足上式。
然后,我们可以看到,如果数组长度不是偶数,是奇数,那么拼接两个该数组时,数组长度就变成了偶数。
如果间隔k不是偶数是奇数,那么跳2次k,就变成了偶数。
因此上式对所有的k和n都成立。
2.当重新回到位置i的时候,我们将i+1,从它开始继续跳,i+1这个位置必然是之前并没有跳到的位置。
证明:
如果能从起点i跳到i+1,然后我们又根据1知道在此之前它经过了i,那么此时遍历的顺序是:
i-->i---i+1,既然i能到i+1,那么正确的顺序应该是i--->i+1--->i----->i+1,不可能在此之前的i跳不到i+1,之后的可以跳到,因此,它应该是一个i--->i+1--->i---->i+1的循环,能满足此条件的只有当k=1时,然而,若k=1,当i重新再次遍历到i时,计数器已到到n,数组所有的数已处理完毕,因此不可能第二次处理i+1。
PS:条件1是我自己证明的,并不知道相关的数学定理叫啥,如果有知道的朋友请私信或者评论。
时间复杂度O(N),空间复杂度O(1)。
更多leetcode算法题解法: 专栏 leetcode算法从零到结束 或者 leetcode 解题目录 Python3 一步一步持续更新~
三.源码
方法一:
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n=len(nums)
k_s=k%n
if k_s==0:return
temp=nums[-k_s::1]
nums[k_s:]=nums[:n-k_s]
nums[:k_s]=temp[:]
方法二:
来源:https://leetcode.com/problems/rotate-array/discuss/454102/Python-O(1)-space
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
k %= len(nums)
def reverse(nums, i, j):
while i < j:
nums[i], nums[j] = nums[j], nums[i]
i += 1
j -= 1
reverse(nums, 0, len(nums)-1)
reverse(nums, 0, k-1)
reverse(nums, k, len(nums)-1)
方法三:
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n = len(nums)
k = k % n
start = 0
count = 0
while count < n:
cur = start
pre = nums[cur]
nxt = (cur + k) % n
temp = nums[nxt]
nums[nxt], pre = pre, nums[nxt]
cur = nxt
pre = temp
count += 1
while start != cur:
nxt = (cur + k) % n
temp = nums[nxt]
nums[nxt], pre = pre, nums[nxt]
cur = nxt
pre = temp
count += 1
start += 1