LeetCode第189题_旋转数组

LeetCode 第189题:旋转数组

题目描述

给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

难度

中等

题目链接

点击在LeetCode中查看题目

示例

示例 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 <= 10^5
  • -2^31 <= nums[i] <= 2^31 - 1
  • 0 <= k <= 10^5

进阶

  • 尝试使用空间复杂度为 O(1) 的原地算法解决这个问题

解题思路

方法一:使用额外数组

最直接的思路是创建一个新数组,然后将原数组中的元素放到新数组中对应的位置,最后将新数组中的元素复制回原数组。

关键点:

  1. 对于数组中的元素 nums[i],将其放到新数组中的 (i + k) % n 位置上,其中 n 是数组的长度
  2. 最后将新数组复制回原数组

时间复杂度:O(n),其中 n 是数组的长度,每个元素被移动一次
空间复杂度:O(n),需要使用长度为 n 的额外数组

方法二:环状替换

为了达到 O(1) 的空间复杂度,我们可以直接在原数组上操作。考虑把原数组当作一个环,从某个位置开始,不断地将当前元素放到其应该在的位置,直到回到起点。

关键点:

  1. 从位置 i 开始,将元素 nums[i] 放到位置 (i + k) % n
  2. 然后从位置 (i + k) % n 的元素开始继续环状替换
  3. 总共需要移动 n 个元素,但在某些情况下会出现环路(即回到起点)
  4. 如果未能移动所有元素,则选择下一个未处理的位置开始新的环路

时间复杂度:O(n),每个元素只会被移动一次
空间复杂度:O(1),只需要常数额外空间

方法三:数组翻转

另一种常用技巧是通过数组翻转来实现旋转。

关键点:

  1. 将整个数组翻转
  2. 将前 k % n 个元素翻转
  3. 将剩余的 n - (k % n) 个元素翻转

时间复杂度:O(n),进行了三次翻转,每次翻转的时间复杂度为 O(n)
空间复杂度:O(1),只需要常数额外空间

代码实现

C# 实现

方法一:使用额外数组
public class Solution {
    public void Rotate(int[] nums, int k) {
        int n = nums.Length;
        k = k % n;  // 处理 k > n 的情况
        
        int[] temp = new int[n];
        for (int i = 0; i < n; i++) {
            temp[(i + k) % n] = nums[i];
        }
        
        Array.Copy(temp, nums, n);
    }
}
方法二:环状替换
public class Solution {
    public void Rotate(int[] nums, int k) {
        int n = nums.Length;
        k = k % n;  // 处理 k > n 的情况
        if (k == 0) return;
        
        int count = 0;  // 记录已经处理的元素个数
        for (int start = 0; count < n; start++) {
            int current = start;
            int prev = nums[start];
            
            do {
                int next = (current + k) % n;
                int temp = nums[next];
                nums[next] = prev;
                prev = temp;
                current = next;
                count++;
            } while (start != current);
        }
    }
}
方法三:数组翻转
public class Solution {
    public void Rotate(int[] nums, int k) {
        int n = nums.Length;
        k = k % n;  // 处理 k > n 的情况
        if (k == 0) return;
        
        // 1. 翻转整个数组
        Reverse(nums, 0, n - 1);
        // 2. 翻转前 k 个元素
        Reverse(nums, 0, k - 1);
        // 3. 翻转剩余元素
        Reverse(nums, k, n - 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--;
        }
    }
}

Python 实现

方法一:使用额外数组
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  # 处理 k > n 的情况
        
        temp = [0] * n
        for i in range(n):
            temp[(i + k) % n] = nums[i]
        
        nums[:] = temp
方法二:环状替换
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  # 处理 k > n 的情况
        if k == 0:
            return
        
        count = 0  # 记录已经处理的元素个数
        for start in range(n):
            if count >= n:
                break
                
            current = start
            prev = nums[start]
            
            while True:
                next_idx = (current + k) % n
                temp = nums[next_idx]
                nums[next_idx] = prev
                prev = temp
                current = next_idx
                count += 1
                
                if start == current:
                    break
方法三:数组翻转
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  # 处理 k > n 的情况
        if k == 0:
            return
        
        # 1. 翻转整个数组
        nums.reverse()
        # 2. 翻转前 k 个元素
        nums[:k] = reversed(nums[:k])
        # 3. 翻转剩余元素
        nums[k:] = reversed(nums[k:])

C++ 实现

方法一:使用额外数组
class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n = nums.size();
        k = k % n;  // 处理 k > n 的情况
        
        vector<int> temp(n);
        for (int i = 0; i < n; i++) {
            temp[(i + k) % n] = nums[i];
        }
        
        nums = temp;
    }
};
方法二:环状替换
class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n = nums.size();
        k = k % n;  // 处理 k > n 的情况
        if (k == 0) return;
        
        int count = 0;  // 记录已经处理的元素个数
        for (int start = 0; count < n; start++) {
            int current = start;
            int prev = nums[start];
            
            do {
                int next = (current + k) % n;
                int temp = nums[next];
                nums[next] = prev;
                prev = temp;
                current = next;
                count++;
            } while (start != current);
        }
    }
};
方法三:数组翻转
class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n = nums.size();
        k = k % n;  // 处理 k > n 的情况
        if (k == 0) return;
        
        // 1. 翻转整个数组
        reverse(nums.begin(), nums.end());
        // 2. 翻转前 k 个元素
        reverse(nums.begin(), nums.begin() + k);
        // 3. 翻转剩余元素
        reverse(nums.begin() + k, nums.end());
    }
};

性能分析

各语言实现的性能对比:

实现语言方法执行用时内存消耗特点
C#方法一256 ms48.2 MB额外空间,实现简单
C#方法二232 ms47.9 MB原地操作,实现复杂
C#方法三208 ms48.0 MB原地操作,实现简单,性能最优
Python方法一248 ms25.5 MB使用切片,简洁易读
Python方法二224 ms25.3 MB实现较复杂
Python方法三188 ms25.4 MB使用内置函数,简洁高效
C++方法一36 ms25.0 MB额外空间
C++方法二28 ms24.8 MB原地操作
C++方法三20 ms24.9 MB使用STL库,简洁高效

补充说明

代码亮点

  1. 方法一简单直观,容易理解和实现
  2. 方法二通过环状替换实现原地操作,节省空间
  3. 方法三使用数组翻转技巧,既简单又高效
  4. 所有方法都考虑了 k > n 的情况,通过取模处理

数组翻转法的原理

数组翻转法的原理可以从数学角度理解。对于原始数组 [1,2,3,4,5,6,7] 和 k = 3:

  1. 全部翻转后得到 [7,6,5,4,3,2,1]
  2. 翻转前 k 个元素,得到 [5,6,7,4,3,2,1]
  3. 翻转后 n-k 个元素,得到 [5,6,7,1,2,3,4]

这正是将数组向右轮转 k 个位置后的结果。这种方法之所以有效,是因为它将数组分成两部分,分别翻转后再整体翻转,相当于实现了两部分的位置交换。

常见错误

  1. 没有处理 k > n 的情况,导致索引越界
  2. 在环状替换方法中,没有正确处理循环,可能导致死循环或某些元素未被处理
  3. 忘记处理 k = 0 的特殊情况
  4. 在翻转法中,翻转范围计算错误

相关题目

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值