目录
-
标签(题目类型):数组,双指针
题目描述
text
给定一个整数数组 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]
提示:
1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
进阶:
尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?
原题:LeetCode 189
思路及实现
方式一:三次翻转
思路
将整个数组翻转,然后分别翻转前k个元素和后n-k个元素,从而达到向右轮转k个位置的效果。
代码实现
Java版本
java
public void rotate(int[] nums, int k) {
k %= nums.length; // 处理k大于数组长度的情况
reverse(nums, 0, nums.length - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.length - 1);
}
private void reverse(int[] nums, int start, int end) {
while (start < end) {
int temp = nums[start];
nums[start] = nums[end];
nums[end] = temp;
start++;
end--;
}
}
// 说明:通过三次翻转实现数组的轮转,时间复杂度O(n),空间复杂度O(1)
C语言版本
c
void reverse(int* nums, int start, int end) {
while (start < end) {
int temp = nums[start];
nums[start] = nums[end];
nums[end] = temp;
start++;
end--;
}
}
void rotate(int* nums, int numsSize, int k){
k %= numsSize;
reverse(nums, 0, numsSize - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, numsSize - 1);
}
// 说明:C语言实现与Java类似,注意数组传递和指针操作
Python3版本
python
def reverse(nums, start, end):
while start < end:
nums[start], nums[end] = nums[end], nums[start]
start += 1
end -= 1
def rotate(nums, k):
k %= len(nums)
reverse(nums, 0, len(nums) - 1)
reverse(nums, 0, k - 1)
reverse(nums, k, len(nums) - 1)
# 说明:Python版本使用切片式交换元素,实现方式与Java和C类似
Golang版本
go
func reverse(nums []int, start, end int) {
for start < end {
nums[start], nums[end] = nums[end], nums[start]
start++
end--
}
}
func rotate(nums []int, k int) {
k %= len(nums)
reverse(nums, 0, len(nums)-1)
reverse(nums, 0, k-1)
reverse(nums, k, len(nums)-1)
}
// 说明:Golang版本使用切片和索引操作,与Java和C的实现思路相同
复杂度分析
- 时间复杂度:O(n),其中n是数组的长度。因为数组中的每个元素都会被翻转两到三次。
- 空间复杂度:O(1),仅使用了常数个额外空间。
方式二:使用辅助数组
思路
使用一个与原数组大小相同的辅助数组,遍历原数组,将元素按照轮转后的位置存放到辅助数组中,最后将辅助数组的元素复制回原数组,完成轮转。
代码实现
Java版本
java
public void rotate(int[] nums, int k) {
k %= nums.length; // 处理k大于数组长度的情况
int[] temp = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
// 将元素放置到新的位置 (i + k) % nums.length
temp[(i + k) % nums.length] = nums[i];
}
// 将新数组的元素复制回原数组
System.arraycopy(temp, 0, nums, 0, nums.length);
}
// 说明:这种方式虽然使用了额外的空间,但代码逻辑较为直观,容易理解
C语言版本
c
void rotate(int* nums, int numsSize, int k){
k %= numsSize;
int* temp = (int*)malloc(numsSize * sizeof(int));
for (int i = 0; i < numsSize; i++) {
temp[(i + k) % numsSize] = nums[i];
}
for (int i = 0; i < numsSize; i++) {
nums[i] = temp[i];
}
free(temp); // 释放辅助数组的内存
}
// 说明:C语言需要手动管理内存,因此在使用完辅助数组后需要释放其内存
Python3版本
python
def rotate(nums, k):
k %= len(nums)
temp = [0] * len(nums)
for i in range(len(nums)):
temp[(i + k) % len(nums)] = nums[i]
nums[:] = temp[:] # 将新列表的元素赋值给原列表
# 说明:Python中可以使用列表推导式或者循环来构造辅助列表,但这里为了保持与其他语言的风格一致,使用了循环
Golang版本
go
func rotate(nums []int, k int) {
k %= len(nums)
temp := make([]int, len(nums))
for i, num := range nums {
temp[(i+k)%len(nums)] = num
}
copy(nums, temp) // 将新切片的元素复制回原切片
}
// 说明:Golang中使用make函数来创建切片,并使用copy函数来复制切片的内容
复杂度分析
- 时间复杂度:O(n),其中n是数组的长度。因为需要遍历原数组一次,并将元素放置到辅助数组和复制回原数组。
- 空间复杂度:O(n),因为使用了一个与原数组大小相同的辅助数组。
总结
方式 | 优点 | 缺点 | 时间复杂度 | 空间复杂度 |
---|---|---|---|---|
方式一 | 空间复杂度低,仅使用常数额外空间 | 逻辑相对复杂,需要进行三次翻转 | O(n) | O(1) |
方式二 | 逻辑直观,易于理解 | 需要使用额外的空间 | O(n) | O(n) |
相似题目
相似题目 | 难度 | 链接 |
---|---|---|
LeetCode 153. 寻找旋转排序数组中的最小值 | 中等 | LeetCode |
LeetCode 154. 寻找旋转排序数组中的最小值 II | 困难 | LeetCode |
LeetCode 143. 重排链表 | 中等 | LeetCode |