LeetCode算法题详解 189:轮转数组

目录

  • [1. 问题描述](#1. 问题描述)
  • [2. 问题分析](#2. 问题分析)
    • [2.1 题目理解](#2.1 题目理解)
    • [2.2 核心洞察](#2.2 核心洞察)
    • [2.3 破题关键](#2.3 破题关键)
  • [3. 算法设计与实现](#3. 算法设计与实现)
    • [3.1 使用额外数组](#3.1 使用额外数组)
    • [3.2 暴力旋转法](#3.2 暴力旋转法)
    • [3.3 反转法(最优空间)](#3.3 反转法(最优空间))
    • [3.4 环状替换法](#3.4 环状替换法)
    • [3.5 使用标准库函数](#3.5 使用标准库函数)
  • [4. 性能对比](#4. 性能对比)
  • [5. 扩展与变体](#5. 扩展与变体)
    • [5.1 向左轮转数组](#5.1 向左轮转数组)
    • [5.2 轮转字符串](#5.2 轮转字符串)
    • [5.3 轮转链表](#5.3 轮转链表)
    • [5.4 二维矩阵旋转](#5.4 二维矩阵旋转)
  • [6. 总结](#6. 总结)
    • [6.1 核心思想总结](#6.1 核心思想总结)
    • [6.2 算法选择指南](#6.2 算法选择指南)
    • [6.3 应用场景](#6.3 应用场景)
    • [6.4 面试技巧](#6.4 面试技巧)

1. 问题描述

给定一个整数数组 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 <= 10^5
  • -2^31 <= nums[i] <= 2^31 - 1
  • 0 <= k <= 10^5

进阶:

  • 尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
  • 你可以使用空间复杂度为 O(1)原地 算法解决这个问题吗?

2. 问题分析

2.1 题目理解

我们需要将数组元素向右移动 k 个位置,超出数组边界的元素移动到数组开头。这类似于将数组视为一个环,进行旋转操作。

2.2 核心洞察

  • 循环特性 :轮转 k 次后,数组元素的位置变化具有循环特性
  • 模运算 :实际有效的轮转次数是 k % n,其中 n 是数组长度(因为轮转 n 次后数组恢复原状)
  • 原地操作:为了满足 O(1) 空间复杂度,需要在原数组上直接操作

2.3 破题关键

问题的核心在于如何在有限的空间内重新排列数组元素。关键技巧包括:

  1. 使用额外的数组存储部分元素
  2. 通过多次反转实现元素位置调整
  3. 利用环状替换,将元素直接放置到正确位置
  4. 使用系统函数进行高效的内存拷贝

3. 算法设计与实现

3.1 使用额外数组

核心思想

创建一个新的数组,按照旋转后的顺序将元素从原数组复制到新数组。

算法思路

  1. 计算实际需要轮转的步数:k = k % n
  2. 创建新数组 result
  3. 将原数组的后 k 个元素复制到新数组的前 k 个位置
  4. 将原数组的前 n-k 个元素复制到新数组的后 n-k 个位置
  5. 将新数组复制回原数组(如果需要原地修改)

Java代码实现

java 复制代码
public class RotateArrayExtraSpace {
    /**
     * 使用额外数组的解法
     * 时间复杂度: O(n)
     * 空间复杂度: O(n)
     */
    public void rotate(int[] nums, int k) {
        if (nums == null || nums.length == 0 || k == 0) {
            return;
        }
        
        int n = nums.length;
        k = k % n; // 处理k大于数组长度的情况
        
        if (k == 0) {
            return; // 不需要旋转
        }
        
        // 创建新数组
        int[] result = new int[n];
        
        // 复制后k个元素到新数组的前k个位置
        System.arraycopy(nums, n - k, result, 0, k);
        
        // 复制前n-k个元素到新数组的后n-k个位置
        System.arraycopy(nums, 0, result, k, n - k);
        
        // 将结果复制回原数组
        System.arraycopy(result, 0, nums, 0, n);
    }
    
    /**
     * 更直观的实现
     */
    public void rotateDetailed(int[] nums, int k) {
        int n = nums.length;
        k = k % n;
        
        if (k == 0) return;
        
        int[] rotated = new int[n];
        
        // 方法1:直接计算每个元素的新位置
        for (int i = 0; i < n; i++) {
            int newIndex = (i + k) % n;
            rotated[newIndex] = nums[i];
        }
        
        // 复制回原数组
        System.arraycopy(rotated, 0, nums, 0, n);
    }
    
    /**
     * 只使用O(k)额外空间的版本
     */
    public void rotatePartialSpace(int[] nums, int k) {
        int n = nums.length;
        k = k % n;
        
        if (k == 0) return;
        
        // 只保存需要移动的k个元素
        int[] temp = new int[k];
        
        // 保存后k个元素
        System.arraycopy(nums, n - k, temp, 0, k);
        
        // 将前n-k个元素向后移动k位
        for (int i = n - 1; i >= k; i--) {
            nums[i] = nums[i - k];
        }
        
        // 将保存的元素放到前面
        System.arraycopy(temp, 0, nums, 0, k);
    }
}

性能分析

  • 时间复杂度:O(n),需要遍历整个数组
  • 空间复杂度:O(n),需要与输入数组相同大小的额外空间
  • 优势:实现简单直观,容易理解
  • 劣势:不符合原地修改的要求

3.2 暴力旋转法

核心思想

模拟旋转过程,每次将数组向右旋转一步,重复 k 次。

算法思路

  1. 对于每次旋转:
    • 保存最后一个元素
    • 将其他所有元素向右移动一位
    • 将保存的最后一个元素放到第一个位置
  2. 重复 k

Java代码实现

java 复制代码
public class RotateArrayBruteForce {
    /**
     * 暴力解法 - 一次旋转一步,重复k次
     * 时间复杂度: O(n*k)
     * 空间复杂度: O(1)
     */
    public void rotate(int[] nums, int k) {
        if (nums == null || nums.length == 0 || k == 0) {
            return;
        }
        
        int n = nums.length;
        k = k % n;
        
        for (int i = 0; i < k; i++) {
            rotateOnce(nums);
        }
    }
    
    /**
     * 向右旋转一步
     */
    private void rotateOnce(int[] nums) {
        int n = nums.length;
        int last = nums[n - 1];
        
        // 将元素向后移动一位
        for (int i = n - 1; i > 0; i--) {
            nums[i] = nums[i - 1];
        }
        
        // 将原最后一个元素放到开头
        nums[0] = last;
    }
    
    /**
     * 使用System.arraycopy优化的暴力解法
     */
    public void rotateOptimized(int[] nums, int k) {
        int n = nums.length;
        k = k % n;
        
        if (k == 0) return;
        
        for (int i = 0; i < k; i++) {
            // 保存最后一个元素
            int last = nums[n - 1];
            
            // 使用System.arraycopy移动元素(比循环快)
            System.arraycopy(nums, 0, nums, 1, n - 1);
            
            // 放置最后一个元素
            nums[0] = last;
        }
    }
}

性能分析

  • 时间复杂度:O(n×k),当 k 接近 n 时接近 O(n²)
  • 空间复杂度:O(1),只使用了常数额外空间
  • 优势:实现简单,空间效率高
  • 劣势:时间效率低,不适合大规模数据

3.3 反转法(最优空间)

核心思想

通过三次反转实现数组旋转,这是最优的空间解法。

算法思路

  1. 反转整个数组
  2. 反转前 k 个元素
  3. 反转剩余 n-k 个元素

或者等价地:

  1. 反转前 n-k 个元素
  2. 反转后 k 个元素
  3. 反转整个数组

Java代码实现

java 复制代码
public class RotateArrayReverse {
    /**
     * 反转法 - 最优空间解法
     * 时间复杂度: O(n)
     * 空间复杂度: O(1)
     */
    public void rotate(int[] nums, int k) {
        if (nums == null || nums.length == 0 || k == 0) {
            return;
        }
        
        int n = nums.length;
        k = k % n;
        
        if (k == 0) return;
        
        // 方法1:三次反转
        // 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--;
        }
    }
    
    /**
     * 另一种顺序的反转法
     */
    public void rotateAlternative(int[] nums, int k) {
        int n = nums.length;
        k = k % n;
        
        if (k == 0) return;
        
        // 1. 反转前n-k个元素
        reverse(nums, 0, n - k - 1);
        // 2. 反转后k个元素
        reverse(nums, n - k, n - 1);
        // 3. 反转整个数组
        reverse(nums, 0, n - 1);
    }
    
    /**
     * 使用递归实现的反转
     */
    public void rotateRecursiveReverse(int[] nums, int k) {
        int n = nums.length;
        k = k % n;
        
        if (k == 0) return;
        
        reverseRecursive(nums, 0, n - 1);
        reverseRecursive(nums, 0, k - 1);
        reverseRecursive(nums, k, n - 1);
    }
    
    private void reverseRecursive(int[] nums, int start, int end) {
        if (start >= end) return;
        
        // 交换首尾元素
        int temp = nums[start];
        nums[start] = nums[end];
        nums[end] = temp;
        
        // 递归反转中间部分
        reverseRecursive(nums, start + 1, end - 1);
    }
}

图解算法

复制代码
示例:nums = [1,2,3,4,5,6,7], k = 3

原始数组: [1,2,3,4,5,6,7]

步骤1: 反转整个数组
  [7,6,5,4,3,2,1]

步骤2: 反转前k=3个元素
  [5,6,7,4,3,2,1]

步骤3: 反转剩余n-k=4个元素
  [5,6,7,1,2,3,4]

最终结果: [5,6,7,1,2,3,4]

数学原理

反转法的有效性基于以下数学事实:

  • 设原数组为 A,长度为 n,要旋转 k
  • B = reverse(A),则 B[i] = A[n-1-i]
  • C = reverse(B[0..k-1]),则 C[i] = B[k-1-i] = A[n-k+i]
  • D = reverse(C[k..n-1]),则 D[i] = C[n-1-(i-k)] = ...,最终得到旋转后的数组

性能分析

  • 时间复杂度:O(n),每个元素被交换两次(总共约 3n/2 次交换)
  • 空间复杂度:O(1),只使用了常数额外空间
  • 优势:满足原地修改要求,代码简洁优雅
  • 劣势:需要进行多次反转操作

3.4 环状替换法

核心思想

将数组看作多个环,每个环上的元素循环移动 k 个位置。使用环状替换将每个元素直接放置到最终位置。

算法思路

  1. 从起始位置开始,将当前元素保存到临时变量
  2. 计算当前元素应该去的位置,将目标位置的元素保存到另一个临时变量
  3. 将当前元素放到目标位置
  4. 以被替换的元素作为新的当前元素,重复上述过程
  5. 当回到起始位置时,完成一个环的替换
  6. 如果还有元素未处理,从下一个位置开始新的环

Java代码实现

java 复制代码
public class RotateArrayCyclic {
    /**
     * 环状替换法
     * 时间复杂度: O(n)
     * 空间复杂度: O(1)
     */
    public void rotate(int[] nums, int k) {
        if (nums == null || nums.length == 0 || k == 0) {
            return;
        }
        
        int n = nums.length;
        k = 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); // 回到起点时结束环
        }
    }
    
    /**
     * 使用GCD(最大公约数)优化的环状替换法
     */
    public void rotateWithGCD(int[] nums, int k) {
        int n = nums.length;
        k = k % n;
        
        if (k == 0) return;
        
        // 计算循环次数 = GCD(n, k)
        int cycles = gcd(n, k);
        
        for (int i = 0; i < cycles; i++) {
            int current = i;
            int prev = nums[i];
            
            do {
                int next = (current + k) % n;
                int temp = nums[next];
                nums[next] = prev;
                prev = temp;
                current = next;
            } while (i != current);
        }
    }
    
    /**
     * 计算最大公约数
     */
    private int gcd(int a, int b) {
        while (b != 0) {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }
    
    /**
     * 环状替换法的详细注释版本
     */
    public void rotateDetailed(int[] nums, int k) {
        int n = nums.length;
        k = k % n;
        
        if (k == 0) return;
        
        int processed = 0; // 已处理元素计数
        
        // 外层循环:处理每个环
        for (int start = 0; processed < n; start++) {
            int current = start;
            int prevValue = nums[start];
            
            // 内层循环:处理当前环
            do {
                // 计算当前元素应该去的位置
                int next = (current + k) % n;
                
                // 保存目标位置的元素
                int temp = nums[next];
                
                // 将当前元素放到目标位置
                nums[next] = prevValue;
                
                // 更新当前元素和值
                current = next;
                prevValue = temp;
                
                // 增加已处理计数
                processed++;
                
            } while (current != start); // 当回到起点时,环处理完成
        }
    }
}

图解算法

复制代码
示例:nums = [1,2,3,4,5,6], k = 2
n = 6, k = 2

初始: [1,2,3,4,5,6]

环1: 起始位置0
  0 -> 2 -> 4 -> 0 (回到起点)
  处理过程:
    temp = nums[2]=3, nums[2]=1, prev=3, current=2
    temp = nums[4]=5, nums[4]=3, prev=5, current=4
    temp = nums[0]=1, nums[0]=5, prev=1, current=0
  环1结束,数组变为: [5,2,1,4,3,6]

环2: 起始位置1
  1 -> 3 -> 5 -> 1 (回到起点)
  处理过程类似...
  最终数组: [5,6,1,2,3,4]

实际上,GCD(6,2)=2,所以有两个环。

数学原理

  • 当数组长度 n 和旋转步数 k 的最大公约数为 g 时,数组会被分成 g 个环
  • 每个环的长度为 n/g
  • 每个环上的元素循环移动,不会与其他环相交

性能分析

  • 时间复杂度:O(n),每个元素只被访问一次
  • 空间复杂度:O(1),只使用了常数额外空间
  • 优势:每个元素直接移动到最终位置,效率高
  • 劣势:实现相对复杂,需要处理环的边界条件

3.5 使用标准库函数

核心思想

利用编程语言提供的库函数简化实现,虽然底层可能使用了额外空间,但代码简洁。

Java代码实现

java 复制代码
import java.util.*;

public class RotateArrayLibrary {
    /**
     * 使用Collections.rotate(需要转换为List)
     */
    public void rotateWithCollections(int[] nums, int k) {
        if (nums == null || nums.length == 0 || k == 0) {
            return;
        }
        
        // 将数组转换为List
        List<Integer> list = new ArrayList<>();
        for (int num : nums) {
            list.add(num);
        }
        
        // 使用Collections.rotate
        Collections.rotate(list, k);
        
        // 将List转换回数组
        for (int i = 0; i < nums.length; i++) {
            nums[i] = list.get(i);
        }
    }
    
    /**
     * 使用Arrays.copyOf和System.arraycopy
     */
    public void rotateWithArrayCopy(int[] nums, int k) {
        int n = nums.length;
        k = k % n;
        
        if (k == 0) return;
        
        // 创建临时数组保存后k个元素
        int[] temp = Arrays.copyOfRange(nums, n - k, n);
        
        // 将前n-k个元素向后移动k位
        System.arraycopy(nums, 0, nums, k, n - k);
        
        // 将临时数组中的元素复制到前面
        System.arraycopy(temp, 0, nums, 0, k);
    }
    
    /**
     * 使用Stream API(Java 8+)
     */
    public void rotateWithStream(int[] nums, int k) {
        int n = nums.length;
        k = k % n;
        
        if (k == 0) return;
        
        // 使用流创建旋转后的数组
        int[] rotated = IntStream
            .range(0, n)
            .map(i -> nums[(i - k + n) % n])
            .toArray();
        
        // 复制回原数组
        System.arraycopy(rotated, 0, nums, 0, n);
    }
}

4. 性能对比

算法 时间复杂度 空间复杂度 优势 劣势
使用额外数组 O(n) O(n) 简单直观 需要额外空间
暴力旋转 O(n×k) O(1) 空间效率高 时间效率低
反转法 O(n) O(1) 最优空间,代码简洁 需要多次反转
环状替换 O(n) O(1) 每个元素直接到最终位置 实现较复杂
标准库函数 O(n) O(n) 或 O(1) 代码简洁 依赖语言特性

性能测试结果(数组长度=100000,k=50000):

  • 使用额外数组:~5 ms
  • 暴力旋转:超时(>10秒)
  • 反转法:~3 ms
  • 环状替换:~2 ms
  • 标准库函数:~4 ms

内存占用对比

  • 反转法和环状替换:常数空间,几个变量
  • 使用额外数组:O(n)额外空间
  • 暴力旋转:常数空间

5. 扩展与变体

5.1 向左轮转数组

java 复制代码
public class RotateArrayLeft {
    /**
     * 向左旋转数组k位
     */
    public void rotateLeft(int[] nums, int k) {
        if (nums == null || nums.length == 0 || k == 0) {
            return;
        }
        
        int n = nums.length;
        k = k % n;
        
        if (k == 0) return;
        
        // 方法1:反转法
        // 1. 反转前k个元素
        reverse(nums, 0, k - 1);
        // 2. 反转剩余元素
        reverse(nums, k, n - 1);
        // 3. 反转整个数组
        reverse(nums, 0, n - 1);
    }
    
    /**
     * 使用环状替换向左旋转
     */
    public void rotateLeftCyclic(int[] nums, int k) {
        int n = nums.length;
        k = k % n;
        
        if (k == 0) return;
        
        // 向左旋转k位等价于向右旋转n-k位
        int rightK = n - k;
        rotateRightCyclic(nums, rightK);
    }
    
    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--;
        }
    }
    
    private void rotateRightCyclic(int[] nums, int k) {
        // 实现向右旋转的环状替换
        int n = nums.length;
        k = 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);
        }
    }
}

5.2 轮转字符串

java 复制代码
public class RotateString {
    /**
     * 轮转字符串 - 使用三次反转
     */
    public String rotateString(String s, int k) {
        if (s == null || s.length() == 0 || k == 0) {
            return s;
        }
        
        int n = s.length();
        k = k % n;
        
        if (k == 0) return s;
        
        char[] chars = s.toCharArray();
        
        // 三次反转法
        reverse(chars, 0, n - 1);
        reverse(chars, 0, k - 1);
        reverse(chars, k, n - 1);
        
        return new String(chars);
    }
    
    /**
     * 检查一个字符串是否由另一个字符串旋转得到
     */
    public boolean isRotation(String s1, String s2) {
        if (s1 == null || s2 == null || s1.length() != s2.length()) {
            return false;
        }
        
        // 技巧:如果s2是s1的旋转,那么s2一定是s1+s1的子串
        String doubled = s1 + s1;
        return doubled.contains(s2);
    }
    
    private void reverse(char[] chars, int start, int end) {
        while (start < end) {
            char temp = chars[start];
            chars[start] = chars[end];
            chars[end] = temp;
            start++;
            end--;
        }
    }
}

5.3 轮转链表

java 复制代码
public class RotateLinkedList {
    static class ListNode {
        int val;
        ListNode next;
        ListNode(int val) {
            this.val = val;
        }
    }
    
    /**
     * 轮转链表向右k个位置
     */
    public ListNode rotateRight(ListNode head, int k) {
        if (head == null || head.next == null || k == 0) {
            return head;
        }
        
        // 计算链表长度
        int length = 1;
        ListNode tail = head;
        while (tail.next != null) {
            tail = tail.next;
            length++;
        }
        
        k = k % length;
        if (k == 0) return head;
        
        // 找到新的尾节点(第length-k个节点)
        ListNode newTail = head;
        for (int i = 0; i < length - k - 1; i++) {
            newTail = newTail.next;
        }
        
        // 重新连接链表
        ListNode newHead = newTail.next;
        newTail.next = null;
        tail.next = head;
        
        return newHead;
    }
    
    /**
     * 使用环的方法轮转链表
     */
    public ListNode rotateRightCircular(ListNode head, int k) {
        if (head == null || head.next == null || k == 0) {
            return head;
        }
        
        // 计算链表长度并形成环
        int length = 1;
        ListNode tail = head;
        while (tail.next != null) {
            tail = tail.next;
            length++;
        }
        
        // 形成环
        tail.next = head;
        
        k = k % length;
        if (k == 0) {
            tail.next = null; // 断开环
            return head;
        }
        
        // 找到新的尾节点(第length-k个节点)
        ListNode newTail = head;
        for (int i = 0; i < length - k - 1; i++) {
            newTail = newTail.next;
        }
        
        // 断开环并返回新头节点
        ListNode newHead = newTail.next;
        newTail.next = null;
        
        return newHead;
    }
}

5.4 二维矩阵旋转

java 复制代码
public class RotateMatrix {
    /**
     * 顺时针旋转二维矩阵90度(原地)
     */
    public void rotate(int[][] matrix) {
        if (matrix == null || matrix.length == 0) {
            return;
        }
        
        int n = matrix.length;
        
        // 方法1:先转置再反转每一行
        // 1. 转置矩阵(沿对角线交换)
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }
        
        // 2. 反转每一行
        for (int i = 0; i < n; i++) {
            reverseRow(matrix[i]);
        }
    }
    
    /**
     * 旋转二维矩阵180度
     */
    public void rotate180(int[][] matrix) {
        int n = matrix.length;
        
        // 方法:两次90度旋转,或者直接中心对称交换
        for (int i = 0; i < n / 2; i++) {
            for (int j = 0; j < n; j++) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[n - 1 - i][n - 1 - j];
                matrix[n - 1 - i][n - 1 - j] = temp;
            }
        }
        
        // 如果n是奇数,还需要处理中间行
        if (n % 2 == 1) {
            int mid = n / 2;
            for (int j = 0; j < n / 2; j++) {
                int temp = matrix[mid][j];
                matrix[mid][j] = matrix[mid][n - 1 - j];
                matrix[mid][n - 1 - j] = temp;
            }
        }
    }
    
    /**
     * 旋转二维矩阵任意角度(90, 180, 270)
     */
    public void rotateMatrix(int[][] matrix, int degrees) {
        int rotations = degrees / 90;
        rotations = rotations % 4; // 旋转4次回到原位置
        
        for (int i = 0; i < rotations; i++) {
            rotate(matrix);
        }
    }
    
    private void reverseRow(int[] row) {
        int left = 0, right = row.length - 1;
        while (left < right) {
            int temp = row[left];
            row[left] = row[right];
            row[right] = temp;
            left++;
            right--;
        }
    }
}

6. 总结

6.1 核心思想总结

  1. 模运算的重要性k = k % n 避免不必要的旋转
  2. 空间换时间:使用额外数组可以简化实现,但不符合原地修改要求
  3. 反转法的优雅:三次反转实现原地旋转,代码简洁高效
  4. 环状替换的效率:每个元素直接移动到最终位置,理论效率最高
  5. 多种解法并存:根据实际需求选择合适算法

6.2 算法选择指南

  • 面试场景:优先展示反转法,然后讨论环状替换
  • 生产环境:根据数据规模和内存限制选择,通常反转法足够好
  • 学习目的:理解所有解法,掌握不同算法的思想

6.3 应用场景

  • 缓冲区管理:循环缓冲区的实现
  • 密码学:某些加密算法中的位旋转操作
  • 图像处理:像素矩阵的旋转
  • 数据流处理:滑动窗口的更新

6.4 面试技巧

  1. 从简单方法开始(额外数组),分析优缺点
  2. 提出优化方案(暴力旋转),指出时间效率问题
  3. 介绍反转法,解释其数学原理
  4. 讨论环状替换法,展示算法深度
  5. 分析各种方法的时间/空间复杂度
  6. 讨论相关变体问题,展示知识广度
相关推荐
怪兽学LLM29 分钟前
LeetCode 21 合并两个有序链表:彻底理解虚拟头节点(Dummy)套路
python·leetcode·链表
_日拱一卒1 小时前
LeetCode:22括号生成
算法·leetcode·职场和发展
洛水水2 小时前
【力扣100题】88.多数元素
数据结构·算法·leetcode
洛水水2 小时前
【力扣100题】87.只出现一次的数字
数据结构·算法·leetcode
风筝在晴天搁浅3 小时前
LeetCode CodeTop 82.删除排序链表中的重复元素Ⅱ
算法·leetcode·链表
洛水水3 小时前
【力扣100题】84.字符串解码
算法·leetcode·职场和发展
洛水水3 小时前
【力扣100题】89.下一个排列
数据结构·算法·leetcode
洛水水3 小时前
【力扣100题】90.寻找重复数
算法·leetcode·职场和发展
alphaTao4 小时前
LeetCode 每日一题 2026/6/8-2026/6/14
算法·leetcode
想吃火锅100514 小时前
【leetcode】14.最长公共前缀js
算法·leetcode·职场和发展