Python每日一练---第五天:轮转数组
Python每日一练来啦,本文已收录于:《Python每日一练》专栏
此专栏目的在于,帮忙学习Python的小白提高编程能力,训练逻辑思维,持续更新中,欢迎免费订阅!!!
-
1. 问题描述
给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]
-
2. 问题分析
有一个数组nums,长度n,向右轮转k次:每个元素向右移动k个位置,超出末尾的元素从头重新出现。
一种最直接的方式是一步一步移动,但是时间复杂度会很高O(n*k),这种方式不可取。
但是观察可以发现:
(1)如果k>n,那么实际移动次数是 k%n,因为移动n次的话,数组回到原状。
(2)轮转k次可以等价为:将整个数组反转,将前k个元素反转,将剩下的n-k个元素反转。
例如:nums=[1,2,3,4,5], k=2
(1)反转整个数组: [5,4,3,2,1]
(2)反转前k个元素:[4,5,3,2,1]
(3)反转剩下的n-k个元素:[4,5,1,2,3]
-
3. 算法思路
思路:
(1)如何k>n 那么k = k%n(这里其实不用判断k是否大于n,直接做这个操作即可)
(2)反转整个数组nums[0:n]
(3)反转前k个元素 nums[0:k]
(4)反转剩余的n-k个元素 nums[k:n]
时间复杂度 O(n); 空间复杂度 O(1)
-
4. 代码实现
from typing import Listclass Solution:def rotate(self, nums: List[int], k: int) -> None:n = len(nums)k = k % ndef reverse(start, end): # 反转的逻辑是: 从最外面两个元素进行翻转,然后逐一向里靠拢while start < end:nums[start], nums[end] = nums[end], nums[start]start += 1end -= 1reverse(0, n-1)reverse(0, k-1)reverse(k, n-1)def simple_rotate(self, nums: List[int], k: int) -> None: ## 使用最简单的方式n = len(nums)k = k % nfor i in range(k):nums.insert(0, nums.pop())def new_arr_rotate(self, nums: List[int], k: int) -> None: ## 使用额外数组进行翻转n = len(nums)k = k % nnew_arr = [0] * nfor i in range(n):new_arr[(i + k) % n] = nums[i]nums[:] = new_arrif __name__ == '__main__':nums = [1,2,3,4,5]Solution().new_arr_rotate(nums, 2)print(nums) 除了代码里的几种常见的算法来解决数组轮转问题外,还有使用切片的方式,利用python切片特性,代码可以非常简洁;还可以使用递归的方式,将数组分成两部分处理,也可以利用双端队列(collections.deque)进行操作。
各个方式进行对比之后,三次反转法应该是效率最大,代码简单,是这里的最优解;
而切片方式,代码最简洁;直接移动方式,效率最低,但最容易理解;额外数组的方式,是最直观易懂的,各个方式各有各的优势、缺点,在实际具体的需求中应该选择合适的算法。
