题干
给定一个整数数组 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]
要注意k是可以大于数组的长度的,当k大于数组的长度时,就代表数组元素轮转了不止一轮。
题解
在第一次做这道题的时候,只想到了空间复杂度为O(n)的做法。遍历数组的每一个元素,根据其当前的位置curPos计算其新的位置newPos = (curPos + k) % len(nums),然后将元素放在一个新的数组中,由于使用了额外的数组,因此空间复杂度为O(n)。
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)
}
在看了官方题解之后发现本题有空间复杂度为O(1)的做法
反转列表
想象一下一个列表轮转了k个位置之后,原本在列表末尾的k个元素将会出现在列表开头,其余的原本在前面的len(nums) - k个元素会出现在列表末尾,为了达成这样的效果,很自然的想到可以把列表反转。但是只反转一次还不够,因为轮转之后的列表的前k个元素和后len(nums) - k个元素仍然是正序的,我们还需要把这两块分别再进行一次反转,把它们的相对顺序恢复,才能得到最后的结果。
以示例1为例,反转一次后获得列表[7,6,5,4,3,2,1],将前3个元素和后5个元素分别再次反转,得到[5,6,7,1,2,3,4]。
这个方法的时间复杂度为O(n)(因为每个元素被交换了2次),空间复杂度为O(1)。
func reverse(nums []int) {
n := len(nums)
for i := 0; i < n/2; i++ {
nums[i], nums[n-i-1] = nums[n-i-1], nums[i]
}
}
func rotate(nums []int, k int) {
n := len(nums)
k %= n
reverse(nums[:])
reverse(nums[:k])
reverse(nums[k:])
}
Swap
第一个方法之所以空间复杂度为O(n),是因为在计算出新的元素位置之后,把它放到了一个新的列表之中。为了避免使用新的列表存放结果,我们可以进行原地操作,在计算出新的元素位置,并执行赋值之前,把这个位置上原来的值存放在一个额外的变量之中,这样就保证了这个元素不会丢失,拿着这个元素,进入下一次循环,再计算这个元素新的位置,如此循环,直到处理完整个数组中所有的元素。
有的时候,只循环一次是不够的,例如当nums = [1,2,3,4,5,6,7,8] k = 2时,数组下标是这样变化的:0->2->4->6->8->0,那么显然数组下标为1、3、5、7的元素不会被处理到,为了应对这种情况,当我们一次循环到头时,如果这个时候还没有完成所有元素的处理,就要以下一个元素为开头再循环一次。还是拿上面这个例子来说,当第一次走完0->2->4->6->8->0,下一次会从下标1开始走完1->3->5->7->1,这样所有的元素就都被处理过了。
func rotate(nums []int, k int) {
n := len(nums)
k %= n
// 使用processed记录已处理的元素数量,当元素数量=n时,退出循环
processed := 0
for start := 0; processed < n; start++ {
// start为当前循环的起始下标, pre记录了当前元素值, cur记录了当前元素下标
// pre和cur都会在本次循环中被不断更新
pre, cur := nums[start], start
// 本次循环的终止条件为当前元素下标 = 起始元素下标
for ok := true; ok; ok = cur != start {
// 计算新的元素下标
next := (cur + k) % n
// 赋值并将已处理的元素数量+1
nums[next], pre, cur = pre, nums[next], next
processed++
}
}
}