题干:
给定一个整数数组 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 = 0
,prev = 1
。 - 计算
next = (current + k) % n = 3
,将nums[3]
的值4
保存到temp
,将nums[3]
更新为1
,prev
更新为4
,current
更新为3
。 - 继续计算
next = (current + k) % n = 6
,将nums[6]
的值7
保存到temp
,将nums[6]
更新为4
,prev
更新为7
,current
更新为6
。 - 继续计算
next = (current + k) % n = 2
,将nums[2]
的值3
保存到temp
,将nums[2]
更新为7
,prev
更新为3
,current
更新为2
。 - 继续计算
next = (current + k) % n = 5
,将nums[5]
的值6
保存到temp
,将nums[5]
更新为3
,prev
更新为6
,current
更新为5
。 - 继续计算
next = (current + k) % n = 1
,将nums[1]
的值2
保存到temp
,将nums[1]
更新为6
,prev
更新为2
,current
更新为1
。 - 继续计算
next = (current + k) % n = 4
,将nums[4]
的值5
保存到temp
,将nums[4]
更新为2
,prev
更新为5
,current
更新为4
。 - 继续计算
next = (current + k) % n = 0
,回到起点start
,退出内层循环。 - 继续下一个起点:
- 从
start = 1
开始,重复上述过程,直到所有元素都被移动。
- 从
复杂度分析:
- 时间复杂度:O(n),每个元素只被移动一次。
- 空间复杂度:O(1),只使用了常数级别的额外空间。