【经典算法】LeetCode 189. 轮转数组(Java/C/Python3/Go实现含注释说明,中等)

目录

题目描述

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
相关推荐
皮皮林5517 小时前
Java性能调优黑科技!1行代码实现毫秒级耗时追踪,效率飙升300%!
java
冰_河8 小时前
QPS从300到3100:我靠一行代码让接口性能暴涨10倍,系统性能原地起飞!!
java·后端·性能优化
地平线开发者8 小时前
SparseDrive 模型导出与性能优化实战
算法·自动驾驶
董董灿是个攻城狮9 小时前
大模型连载2:初步认识 tokenizer 的过程
算法
地平线开发者9 小时前
地平线 VP 接口工程实践(一):hbVPRoiResize 接口功能、使用约束与典型问题总结
算法·自动驾驶
罗西的思考9 小时前
AI Agent框架探秘:拆解 OpenHands(10)--- Runtime
人工智能·算法·机器学习
桦说编程11 小时前
从 ForkJoinPool 的 Compensate 看并发框架的线程补偿思想
java·后端·源码阅读
躺平大鹅12 小时前
Java面向对象入门(类与对象,新手秒懂)
java
HXhlx13 小时前
CART决策树基本原理
算法·机器学习