===问:
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入:
[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:
输入:
[-1,-100,3,99] 和 k = 2
输出:
[3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
说明:
- 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
- 要求使用空间复杂度为 O(1) 的 原地 算法。
===答:
方法一:执行34.66%,内存16.98%
创建一个切片temp
,遍历nums
,根据条件将值一个一个放入temp
func rotate1(nums []int, k int) {
l := len(nums)
//新建一个临时切片
temp := make([]int, l, l)
// 去掉所有元素都循环过的次数
k = k % l
// fmt.Println(k)
//不需要循环直接跳出
if k == 0 {
return
}
for i := range nums {
j := i + k
if j < l {
temp[j] = nums[i]
} else if j > l {
temp[j-l] = nums[i]
} else {
temp[0] = nums[i]
}
// fmt.Println(temp, nums[i])
// temp = append(temp[l-1:], temp[:l-1]...)
}
//还给原切片
for i, v := range temp {
nums[i] = v
}
// fmt.Printf("%T %v\n", nums, nums)
}
方法二:执行95.39%,内存9.43%
利用内置函数append()
实现,但由于每次append()
,内存地址都会改变,因此也需要启用一个临时切片temp
。
只能使用append(nums[:0],temp...)
这种形式来保证nums
的地址不会改变。
func rotate2(nums []int, k int) {
l := len(nums)
k = k % l
if k == 0 {
return
}
// 效果与下面的评测结果一致
// nums = append(nums[:0], (append(nums[l-k:], nums[:l-k]...))...)
//每一次append,内存地址都会改变,
//新建一个临时切片
temp = append(nums[l-k:], nums[:l-k]...)
//还给原切片
nums = append(nums[:0], temp...)
}
方法三:执行8.57%,内存64.15%
这个思路就比较简单了,nums
元素的前一个值替换后一个,最后的值替换第一个。
这个方法都在nums
中操作,由于少了一个临时切片,内存开销大大降低。
func rotate3(nums []int, k int) {
l := len(nums)
k %= l
// fmt.Println(k)
if k == 0 {
return
}
var temp int
for i := 0; i < k; i++ {
temp = nums[l-1]
for j := l - 1; j >= 0; j-- {
if j == 0 {
nums[0] = temp
} else {
nums[j] = nums[j-1]
}
}
}
}
方法四:执行18.24%,内存100%
这个方法参考了内存占用排名第一的代码,与方法三几乎无疑,只是多了一个临时变量,并将nums[j] = num[j-1]
替换成了nums[j]=t1
,利用变量t1
和t2
之间进行替换,而不像方法三中切片直接nums[n]=nums[n-1]
交换值,虽然表面上多了一个变量,实际内存却占用更小。
func rotate4(nums []int, k int) {
l := len(nums)
k %= l
// fmt.Println(k)
if k == 0 {
return
}
var t1, t2 int
for i := 0; i < k; i++ {
t1 = nums[l-1]
for j := 0; j < l; j++ {
t2 = nums[j]
nums[j] = t1
t1 = t2
}
}
}
===解:
这道题到手的时候一度感觉特别简单,结果四个解题方法做了一天。。。真是够菜的!
其中注意的地方主要有:
- 虽然有
append(nums[:0],temp)
这种形式来防止重置内存地址(前提是temp
内的元素个数与nums
中的一致),但这也提醒我们实际使用append()
时需密切注意内存的变化。 - 在大数据量的切片中更新值,某些情况宁可使用一个临时变量来存值,而不要用索引的方法互相交换值(方法四)
- 循环的思路要多多变换,比如方法一和方法三的循环相比,一看就知道方法一够菜!~~哈