189. 轮转数组

189. 轮转数组

📚核心逻辑

本解法采用 三次翻转(数组反转)的策略:

  1. 整体翻转:先将整个数组进行翻转,这样原本在末尾的元素就移动到了开头。
  2. 局部翻转(前 k 个):将前 k 个元素进行翻转,恢复这部分的正确顺序。
  3. 局部翻转(剩余部分):将剩下的元素进行翻转,恢复这部分的正确顺序。 这样通过三次原地翻转,即可在不使用额外空间的情况下完成数组的轮转。

🔥代码实现(含详细变量注释)

class Solution {
    public void rotate(int[] nums, int k) {
        // n:记录当前数组的长度
        int n = nums.length;
        
        // k:经过取模运算,得到实际需要向右轮转的真实步数(避免重复轮转)
        k = k % n;
        
        // 如果实际轮转步数为0,说明无需轮转,直接返回结束方法
        if (k == 0) {
            return;
        }
        
        // 步骤1:翻转整个数组 (索引 0 到 n-1)
        Helper(nums, 0, n - 1);
        
        // 步骤2:翻转前 k 个元素 (索引 0 到 k-1)
        Helper(nums, 0, k - 1);
        
        // 步骤3:翻转剩下的元素 (索引 k 到 n-1)
        Helper(nums, k, n - 1);
    }
    
    /**
     * 翻转数组中指定区间的元素
     * @param nums  需要操作的原始数组
     * @param start 要翻转区间的起始索引
     * @param end   要翻转区间的结束索引
     */
    public void Helper(int[] nums, int start, int end) {
        // 双指针循环:只有当左边索引小于右边索引时才进行交换,直到两指针相遇
        while (start < end) {
            // exchange:用于交换两个元素的临时中转变量
            int exchange = nums[start];
            
            // 将右侧(end位置)的值覆盖到左侧(start位置)
            nums[start] = nums[end];
            
            // 将暂存的左侧原值赋值给右侧(end位置),完成一次交换
            nums[end] = exchange;
            
            // 交换完成后,左指针向右移动一位
            start++;
            
            // 交换完成后,右指针向左移动一位
            end--;
        }
    }
}
  • ⏱️复杂度分析
    • 时间复杂度:O(N),其中 N 是数组的长度。每个元素最多被翻转两次,总时间复杂度为 O(N)。

    • 空间复杂度:O(1),使用了常数个变量(n、k、exchange)进行原地交换,没有使用额外的数组空间。

🔍反思一下:

✨1. 这个题其实很简单,假设不是 n 的倍数的情况下(n 的倍数相当于没反转),不论你 k 是s多少次,都有一套公式可以直接解决,而且只需要反转三次

image-jfwC.png

✨2. 还发现一个现象就是对于数组的一些操作吧,其实我们可以设置区间来进行操作,这样思路清晰,代码量会小很多,比如滑动窗口(处理连续子数组 / 子串等等)