q48 旋转图像
题解
这道题要求不能使用额外的数组,不然就是一道简单题,官方题解给了两种可行的做法:
-
原地旋转数组:
通过分析我们可以发现,原来在a[i][j]的元素经过旋转以后,位置就变成了a[j][n - i - 1],所以我们就得到了以下的等式:a[j][n - i - 1] = a[i][j]
;根据这个性质我们不难推出,a[j][n - i - 1]所在的元素,经过旋转以后,位置就变成了a[n - i - 1][n - j - 1],进而得到等式:a[n - i - 1][n - j - 1] = a[j][n - i - 1]
;而a[n - i - 1][n - j - 1]位置的元素旋转以后位置就变成了a[n - j - 1][i],得到等式:a[n - j - 1][i] = a[n - i - 1][n - j - 1]
;a[n - j - 1][i]位置的元素旋转以后又变回了a[i][j],得到等式:a[i][j] = a[n - j - 1][i]
。现在元素位置的改变成为了一个闭环,接着每次循环,我们就可以一次性对四个位置的值做一次变换,刚好符合题目的要求。
但是我们还有一个问题:如何确定循环的次数?其实很简单,因为我们每循环一次就要改变四个位置的元素,所以当n为偶数时需要循环(n ^ 2) / 4 = (n / 2) * (n / 2)次;当n为奇数时需要循环(n ^ 2 - 1) / 4 = ((n - 1) / 2) * ((n + 1) / 2)次。func rotate(matrix [][]int) { n := len(matrix) for i := 0; i < n / 2; i++ { for j := 0; j < (n + 1) / 2; j++ { matrix[i][j], matrix[j][n - i - 1], matrix[n - i - 1][n - j - 1], matrix[n - j - 1][i] = matrix[n - j - 1][i], matrix[i][j], matrix[j][n - i - 1], matrix[n - i - 1][n - j - 1] } } }
-
转化为转置数组求解:
我们可以先将数组进行水平翻转,经过水平翻转以后我们可以发现,目标数组与水平翻转以后的数组只差转置这一个步骤。
func rotate(matrix [][]int) { n := len(matrix) // 水平翻转 for i := 0; i < n / 2; i++ { for j := 0; j < n; j++ { matrix[i][j], matrix[n - i - 1][j] = matrix[n - i - 1][j], matrix[i][j] } } // 转置 for i := 0; i < n; i++ { for j := 0; j < i; j++ { matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] } } fmt.Println(matrix) }
q136 只出现一次的数字
题解
题目要求:线性复杂度 + 0额外的空间复杂度
开始我打算先对数组进行排序,然后再用两个指针去遍历,但是时间复杂度还是很高:
func singleNumber(nums []int) int {
if len(nums) == 1 {
return nums[0]
}
sort.Ints(nums)
for i, j := 0, 1; i < len(nums); {
if i == len(nums) - 1 {
return nums[i]
}
if nums[i] != nums[j] {
return nums[i]
}
i += 2
j += 2
}
return 1
}
后面看了题解才发现可以使用异或运算:
- 任何数和0做异或运算,结果是本身
- 任何数和自己做异或运算,结果是0
func singleNumber(nums []int) int {
var res int
for _, v := range nums {
res ^= v
}
return res
}
q169 多数元素
题解
这道题使用hash表来做非常简单,但是空间复杂度就不为常数级了。我们采用的方法叫做摩尔投票法,假设我们把数组遍历一遍,当遍历到那个众数,就加上1,反之就加上-1.那么最终的答案一定会大于0。所以我们可以维护一个res结果变量和一个count计数器,当count0的时候就让res=nums[i],resnums[i]的时候就累加计数器,反之就递减计数器。因为众数大于数组长度的一半,所以计数器在不断变为0的过程中,实际上会把不是众数的数给淘汰掉,最后res保存的变量,肯定就是那个众数。
func majorityElement(nums []int) int {
// 摩尔投票法
res, count := -1, 0
for _, v := range nums {
if count == 0 {
res = v
}
if v == res {
count++
} else {
count--
}
}
return res
}
q448 找到所有数组中消失的数字
题解
第一步可以遍历一次数组,把所有数字先移动到它应该出现的位置上,比如 4 3 2 1
,我们将他移动成1 2 3 4
,如果数字有重复比如1 4 2 2 3
,重复的数字就不要移动:1 2 3 4 2
,那么通过下标为4的这个位置的元素为2,可以得知缺失的数字是4 + 1 = 5.
在第一次遍历的时候,我们规定以下情况不予交换:
- 遍历到的数字出现在了它应该出现的位置.
- 遍历的数字没有出现在它应该出现的位置,但是它应该出现的位置已经有了和它一样的数字.
交换完毕之后,再遍历一次数组,即可快速找出不在数组中的数字.
func findDisappearedNumbers(nums []int) (res []int) {
i := 0
for i < len(nums) {
// 如果当前元素nums[i]正好出现在他应该出现的地方
if nums[i] == i+1 {
i++
continue
}
// 如果遍历到的元素,它应该出现的位置上已经有了一个与他一摸一样的数,说明重复了
// 应该出现位置的数
itemPos := nums[i] - 1
if nums[i] == nums[itemPos] {
i++
continue
}
// 否则就交换
nums[i], nums[itemPos] = nums[itemPos], nums[i]
}
for i, _ := range nums {
if nums[i] != i+1 {
res = append(res, i+1)
}
}
return
}
剑指offer 03.数组中重复的数字
题解
这道题有两种解决方法。
第一种是最简单的,遍历数组,然后用集合来记录出现过的元素。
第二种是原地置换法,因为数组的长度为n,而且数组里的所有数字都在 0~n-1
的范围内,所以元素应该对应一个下标,如果有元素重复了,那么就会出现一种情况,就是一个下标会对应两个元素,我们采取的策略是,遍历数组,将遍历到的数复制给它对应的那个下标的位置,最后如果遍历到一个数,发现在以它为下标的位置已经有了一个与该下标相同的数,这个数就是重复的数字。
第一种解法:
func findRepeatNumber(nums []int) int {
hashTable := make(map[int]struct{})
for _, num := range nums {
if _, ok := hashTable[num]; ok {
return num
}
hashTable[num] = struct{}{}
}
return -1
}
第二种解法:
func findDuplicate(nums []int) int {
for i := 0; i < len(nums); {
if i == nums[i] {
i++
continue
}
if nums[i] == nums[nums[i]] {
return nums[i]
}
nums[i], nums[nums[i]] = nums[nums[i]], nums[i]
}
return -1
}
剑指 Offer 39. 数组中出现次数超过一半的数字
题解
这道题其实就是多数元素,可以使用摩尔投票法来做,就是当遍历到一个值的时候,用变量res记录下来,遍历到下一个元素的时候,与res做比较,如果相等,就将预先定义好的count变量++,如果不相等,就将count–,表示将当前变量与不相等的变量相抵消。
我们在循环里面进行判断,当count == 0
的时候,表示res记录的变量被抵消完了,所以肯定不是数组中个数超过一半的元素,就将res切换为新的遍历到的数。
func majorityElement(nums []int) int {
// 使用摩尔投票法
res, count := -1, 0
for _, v := range nums {
// 当count说明原来的元素已经被不同的元素抵消完,开始给res赋予新的值
if count == 0 {
res = v
}
// 遇见相同的元素就count++
// 遇见不同的元素就将count--,表示和不同的元素相抵消
if res == v {
count++
} else {
count--
}
}
return res
}