题目传送门:JZ3 数组中重复的数字
解法一:排序法,先给数组排序,然后比较第i个元素和第i+1个元素是否相等,如果相等则说明有重复的数字。
这种解法的时间复杂度是排序的时间复杂度,最小时间复杂度是O(nlogn),所有操作都在原数组上进行,只需要额外的排序指针,空间复杂度是O(1)。
解法二:哈希表法,利用哈希表可以在O(1)时间内快速查找的性质。从头到尾扫描数组,并把数组的数字添加到哈希表内,添加数字到哈希表前先查找数字是否在哈希表内,如果不在,则添加数字;如果在,则说明在此之前已经添加过这个数字进去,发现重复的数字了,并返回这个重复数字。
这种解法的时间复杂度是O(n),也就是扫描数组的时间复杂度,查找哈希表的时间是O(1)不影响从到到尾扫描数组的时间复杂度,空间复杂度也是O(n),需要创建一个和数组同等规模的哈希表存储数组的元素进行查找。这种解法优于排序法,时间复杂度一般优先于空间复杂度。
解法三:重排数组法,这里的重排是说把数组重排成numbers[i]=i,也就是数组元素的值等于下标的值。回顾题目的条件,数组的数字都在【0,n-1】的范围内,解法一和解法二都没有利用这一条件,重排数组利用上这个条件可以实现时间复杂度O(n)和空间复杂度O(1)。
如果数组中没有重复的数字,那么数字i将出现在数组的第i个位置上,如果有重复的数字,则一个位置上可能有多个元素,有的位置没有元素。
关键是如何实现重排数组
从头到尾扫描数组的每个元素,如果numbers[i]=i,那就跳过这个元素接着扫描下一个元素,如果numbers[i]!=i,就比较numbers[i]和numbers[numbers[i]],如果两数相等,说明有重复数,这个重复数就是numbers[i].如果不相等,就交换numbers[i]和numbers[numbers[i]]。然后继续进行这个下标i位置上的比较和交换,不停的比较numbers[i]和numbers[numbers[i]],直到numbers[i]=i或者numbers[i]=numbers[numbers[i]]
这里应当有个隐藏的结论,我目前还没有理解这个结论,也是解法三的前提,即如果数组元素都不重复,使得数组数字等于下标,只需要不停的交换数组数字和以数组数字为下标的位置的数字,即进行swap(numbers[i],numbers[numbers[i]])的操作,就可以把i放到他应该的位置上。关键字,数组下标排序,之后补上结论的证明