Leetcode-找规律题


q48 旋转图像


题目传送门


题解

这道题要求不能使用额外的数组,不然就是一道简单题,官方题解给了两种可行的做法:

  1. 原地旋转数组:
    通过分析我们可以发现,原来在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]
    		}
    	}
    }
    

  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
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值