【经典算法】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
相关推荐
飞翔中文网几秒前
读RESTful有感,关于Java接口设计规范的说明
java·restful·设计规范
Byron__6 分钟前
Spring核心面试知识点(IoC/Bean生命周期/AOP/事务)
java·spring·面试
wuweijianlove10 分钟前
算法中的记忆化思想与重复子问题优化的技术5
算法
AI人工智能+电脑小能手14 分钟前
【大白话说Java面试题 第78题】【Mysql篇】第8题:解释下最左前缀原则?
java·开发语言·数据库·mysql·面试
一起逃去看海吧35 分钟前
对接LangSmith
java·前端·数据库
wyhwust35 分钟前
web应用技术-第一次课后作业
java·前端·数据库
java1234_小锋38 分钟前
什么是 RAG(检索增强生成)?请简述 Spring AI 实现 RAG 的完整流程,包括涉及的核心组件。
java·人工智能·spring·rag
砍材农夫1 小时前
物联网 基于netty核心实战-心跳保活机制
java·后端·物联网·struts·servlet·netty
小江的记录本1 小时前
【JVM虚拟机】垃圾回收GC:垃圾判定算法:引用计数法、可达性分析算法(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·算法·spring·面试
彦楠1 小时前
指定Tomcat运行的JDK地址
java·开发语言·tomcat