LeetCode 热题 HOT 100(普通数组)189. 轮转数组

42 阅读4分钟

题目描述

189. 轮转数组

给定一个整数数组 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) 的 原地 算法解决这个问题吗?

代码实现 Solve 1 使用额外的数组

func rotate(nums []int, k int)  {
    n := len(nums)
    numsCopy := make([]int, len(nums))
    copy(numsCopy, nums)
    for i := 0; i < n; i++ {
        nums[(i+k)%n] = numsCopy[i]
    }
}

时空复杂度

时间复杂度和空间复杂度均为 O(n)

优化代码实现 Solve

将空间复杂度优化为 O(1)。

代码实现 Solve 2 多次翻转

func rotate(nums []int, k int)  {
    n := len(nums)
    distance := k % n
    revert(nums, 0, n)
    revert(nums, 0, distance)
    revert(nums, distance, n)
}

func revert(nums []int, start, end int) {
    for i := 0; i < (end-start)/2; i++ {
        nums[start+i], nums[end-i-1] = nums[end-i-1], nums[start+i]
    }
}
  1. 翻转整个数组
  2. 翻转前 k 个元素
  3. 翻转剩余元素

关键状态变化图

步骤操作数组状态说明
1初始状态[1,2,3,4,5,6,7]原始数组
2处理 kk = 3 % 7 = 3k 不需要取模,仍为 3
3反转整个数组[7,6,5,4,3,2,1]将整个数组反转
4反转前 k 个元素[5,6,7,4,3,2,1]将前 3 个元素反转
5反转剩余元素[5,6,7,1,2,3,4]将后 4 个元素反转
6返回结果[5,6,7,1,2,3,4]最终结果

循环替换

通过循环替换元素实现旋转,每次将一个元素放到其最终位置,并将该位置原来的元素保存用于下一次替换。

func rotate(nums []int, k int)  {
    n := len(nums)

    processed := 0
    for start := 0; processed < n; start++ {
        current := start
        prev := nums[start]

        // 多步长交换
        for {
            next := (current + k) % n
            tmp := nums[next]
            nums[next] = prev

            current = next
            prev = tmp
            processed++

            // 回到起始位置
            if current == start {
                break
            }
        }
    }
}

关键状态变化图

步骤当前位置(current)当前值(prev)下一位置(next)下一值(temp)数组状态已处理元素(count)
10134[1,2,3,1,5,6,7]1
23467[1,2,3,1,5,6,4]2
36723[1,2,7,1,5,6,4]3
42356[1,2,7,1,5,3,4]4
55612[1,6,7,1,5,3,4]5
61245[1,6,7,1,2,3,4]6
74501[5,6,7,1,2,3,4]7
80131回到起始位置,结束当前循环7

考察知识点

这道题主要考察以下知识点:

  1. 数组操作:理解数组元素的移动和旋转。

  2. 原地算法:能够在 O(1) 空间复杂度下解决问题,不使用额外数组空间。

  3. 多种解题思路:能够提出多种不同的解决方案,体现算法设计的灵活性。

  4. 取模运算:处理 k 大于数组长度的情况,使用取模运算 k % n 来简化问题。

  5. 数学思维:特别是多次反转法,利用数学性质来简化问题。

  6. 时间复杂度分析

    • 所有三种方法的时间复杂度都是 O(n),其中 n 是数组长度。
  7. 空间复杂度分析

    • 额外数组法:O(n)
    • 多次反转法:O(1)
    • 循环替换法:O(1)
  8. 边界条件处理:处理数组为空、k 为 0、k 大于数组长度的情况。

  9. 循环和迭代:在循环替换法中,理解如何跟踪已处理的元素和循环的终止条件。

  10. 原地交换:使用变量交换数组元素,如 nums[start], nums[end] = nums[end], nums[start]

这道题展示了一个简单问题可以有多种解决方案,而且可以从空间复杂度为 O(n) 的简单解法逐步优化到空间复杂度为 O(1) 的高效解法。多次反转法是一种特别巧妙的解法,它利用了数组反转的性质来完成旋转操作。