【算法】剑指第二版3.数组中重复数字 题干 直觉思路 分析后才能得到的思路 怎么想到的 题目分析 代码编写思路 速记

在一个长度为n的数组里的所有数字都在0到n-1的范围内。数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。
例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

直觉思路

  1. 排序 O(nlogn): O(nlogn)排序+O(n)遍历查找

  2. 哈希表 O(n)+O(n)

分析后才能得到的思路

  1. 在没有重复数字的情况下,每个位置的数字应该和下标对应相等。

怎么想到的

这个思路怎么想到的,暂时难以清晰描述出来,只能观察题目的特点,进行思路尝试。

PS. 注意区分排序后的位置属于它的位置,假设没有重复,则每个数都有属于自己的位置。 如果有重复的数,则会出现2个数放到同一个位置的情况,也就发现了重复元素。

题目分析

[1(cur),0,1]
cur(1)的位置不对,和位置1的元素交换 -> [0,1,1]
cur(0)的位置正确,cur前进 -> [0,1(cur),1]
cur(1)的位置正确,cur前进 -> [0,1,1(cur)]
cur(1)的位置不对,交换前进行检查,发现目标位置已经有该元素,即此时的cur就是重复数字。

代码编写思路

基于[101]的分析:
从描述中,可以分享有个外循环负责遍历,内部有while循环直到cur位置的元素放在了正确位置才结束,如果不在正确位置就要交换到正确位置,交换位置之前需要检查cur位置和交换位置的值是否相等,相等则找到重复,不相等则交换。

func FindAnyDuplicates(nums []int) int {
	// 5. 输入空检查
	if len(nums) == 0 || nums == nil {
		return -1
	}
	for _, v := range nums {
		if v < 0 || v > len(nums)-1 { // 4.输入合理性检查 数字范围[0,n-1]
			return -1
		}
	}
	// 1. 遍历数组
	for i, _ := range nums {
                // 2. 内循环直到把cur位置放的就是cur值才结束
		for nums[i] != i {
			// 3. 如果交换的目标位置值就是cur值,说明出现重复
			if nums[i] == nums[nums[i]] {
				return nums[i]
			}
			// 3. 把当前元素交换到属于它的位置 // YC: 不是放到排序后的位置 // YC:假设数组没有重复,则cur就被交换到了排序后的位置 // TODO:在有重复元素的情况下,并不是交换到属于他的位置。比如[23344],把2交换位置2[33244],而位置2显然不是排序后2的位置,而是属于2的位置。
			nums[i], nums[nums[i]] = nums[nums[i]], nums[i]
		}
	}
	return -1
}
  • 时间复杂度

YW: 你自己拿个数组试一试就知道,这种写法,最坏情况,也就是交换两次的时候是:第一次被交换(另一个数回到自己的下标)等下一次在用到它,就该它跟别人换,就到自己数值的那个下标了。


我本来是想用常规思路分析复杂度,先外层O(n),然后内层常数次,但是这个不太好使。
所以2个循环一起分析,因为每个数最多交换2次就能找到对的位置,所以这2个循环整体执行交换操作的次数不超过2n,所以就是O(n)。

【算法】剑指第二版3.数组中重复数字
题干
直觉思路
分析后才能得到的思路
怎么想到的
题目分析
代码编写思路
速记

速记

每个位置的数字与其对应的下标相等