leetcode 189. Rotate Array O(1)内存 python3

一.问题描述

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

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值