【Golang】LeetCode面试经典150题:189. 轮转数组

题干:

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

如:

输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]

解法1:三次翻转

func rotate(nums []int, k int) {
    n := len(nums)
    if n == 0 {
        return
    }
    k = k % n
    reverse(nums, 0, n-1)
    reverse(nums, 0, k-1)
    reverse(nums, k, n-1)
}

func reverse(nums []int, start, end int) {
    for start < end {
        nums[start], nums[end] = nums[end], nums[start]
        start++
        end--
    }
}

解析1:

三次反转操作的目的是通过分段反转来实现整个数组的轮转。这种方法的时间复杂度为 O(n),且不需要额外的空间,是一种非常高效的解决方案。

具体步骤:

1. 反转整个数组

reverse(nums, 0, n-1)

这一步将整个数组反转。例如,对于数组 [1, 2, 3, 4, 5, 6, 7],反转后得到 [7, 6, 5, 4, 3, 2, 1]

2. 反转前 k 个元素

reverse(nums, 0, k-1)

这一步将前 k 个元素反转。例如,对于 k = 3,反转后得到 [5, 6, 7, 4, 3, 2, 1]

3. 反转剩下的元素

reverse(nums, k, n-1)

这一步将剩下的元素反转。例如,对于 k = 3,反转后得到 [5, 6, 7, 1, 2, 3, 4]

复杂度分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

解法2:使用额外的数组

func rotate(nums []int, k int) {
    newNums := make([]int, len(nums))
    for i, v := range nums {
        newNums[(i+k)%len(nums)] = v
    }
    copy(nums, newNums)
}

解析2:

这个解法很简单粗暴,直接挨个元素复制到新数组中

复杂度分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

解法3:环状替换

func rotate(nums []int, k int) {
    n := len(nums)
    k = k % n
    count := 0
    
    for start := 0; count < n; start++ {
        current := start
        prev := nums[start]
        
        for {
            next := (current + k) % n
            temp := nums[next]
            nums[next] = prev
            prev = temp
            current = next
            count++
            
            if start == current {
                break
            }
        }
    }
}

解析3:

1.计算 k

k = k % n:计算实际需要轮转的位置数。k = k % n 的意思是将 k 对 n 取模,即计算 k 除以 n 的余数。这个操作的目的是确保 k 的值在合理的范围内,避免不必要的轮转

  • 例如:
  • 如果 nums 的长度 n 为 7,k 为 3,那么 k % n 结果为 3。
  • 如果 nums 的长度 n 为 7,k 为 10,那么 k % n 结果为 3,因为轮转 10 个位置等同于轮转 3 个位置(10 % 7 = 3)。

2.环状替换

  • 使用一个计数器 count 来记录已经移动的元素个数。
  • 外层循环从 start 开始,直到所有元素都被移动(即 count < n)。
  • 内层循环从 start 开始,将当前元素 prev 移动到它最终的位置 next,并将 next 位置的元素保存到 temp,然后将 prev 更新为 temp,继续移动下一个元素。
  • 如果回到起点 start,则退出内层循环,继续下一个起点。

每次交换的时候temp都相当于一个临时存储位置,因为next即将被替换。用c语言做过数据结构的话应该熟悉这个交换的方式。

具体步骤

假设 nums = [1, 2, 3, 4, 5, 6, 7] 且 k = 3,我们来看看每一步的具体操作:

1.计算 k

  k = k % n 结果为 3,因为 3 % 7 = 3
2.环状替换

  • 从 start = 0 开始,current = 0prev = 1
  • 计算 next = (current + k) % n = 3,将 nums[3] 的值 4 保存到 temp,将 nums[3] 更新为 1prev 更新为 4current 更新为 3
  • 继续计算 next = (current + k) % n = 6,将 nums[6] 的值 7 保存到 temp,将 nums[6] 更新为 4prev 更新为 7current 更新为 6
  • 继续计算 next = (current + k) % n = 2,将 nums[2] 的值 3 保存到 temp,将 nums[2] 更新为 7prev 更新为 3current 更新为 2
  • 继续计算 next = (current + k) % n = 5,将 nums[5] 的值 6 保存到 temp,将 nums[5] 更新为 3prev 更新为 6current 更新为 5
  • 继续计算 next = (current + k) % n = 1,将 nums[1] 的值 2 保存到 temp,将 nums[1] 更新为 6prev 更新为 2current 更新为 1
  • 继续计算 next = (current + k) % n = 4,将 nums[4] 的值 5 保存到 temp,将 nums[4] 更新为 2prev 更新为 5current 更新为 4
  • 继续计算 next = (current + k) % n = 0,回到起点 start,退出内层循环。
  • 继续下一个起点
    • 从 start = 1 开始,重复上述过程,直到所有元素都被移动。

复杂度分析:

  • 时间复杂度:O(n),每个元素只被移动一次。
  • 空间复杂度:O(1),只使用了常数级别的额外空间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值