Golang面试考题记录 ━━ 旋转数组 ~~ 执行95.39%和内存100%的抉择

===问:

给定一个数组,将数组中的元素向右移动 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,利用变量t1t2之间进行替换,而不像方法三中切片直接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
		}
	}
}

===解:

这道题到手的时候一度感觉特别简单,结果四个解题方法做了一天。。。真是够菜的!

其中注意的地方主要有:

  1. 虽然有append(nums[:0],temp)这种形式来防止重置内存地址(前提是temp内的元素个数与nums中的一致),但这也提醒我们实际使用append()时需密切注意内存的变化。
  2. 在大数据量的切片中更新值,某些情况宁可使用一个临时变量来存值,而不要用索引的方法互相交换值(方法四)
  3. 循环的思路要多多变换,比如方法一和方法三的循环相比,一看就知道方法一够菜!~~哈
发布了44 篇原创文章 · 获赞 1 · 访问量 3595

猜你喜欢

转载自blog.csdn.net/snans/article/details/105169811