剑指offer--数组中重复的数字
题目:找出数组中重复的数字
在一个长度为 n 的数组里的所有数字都在 0 ~ n - 1 范围内,数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
例如:数组 { 2 , 3 , 1 , 0 , 2 , 5 ,3 } ,对应输出为 2 或者 3。
思路:
- 直接对数组排序,再遍历数组找到重复值,排序时间复杂度 O(nlogn);
- 利用哈希表,从头扫描数组,每扫描到一个数字,判断哈希表是否已经包含了该数字;若未包含,就加入哈希表;若包含,就找到了一个重复数字,时间复杂度 O(n),空间复杂度 O(n);
- 数组中数字都在 0 ~ n - 1 范围内,若数组中没有重复数字,那么数组排序后数字 i 将出现在下标为 i 的位置;由于数组中有重复的数字,所以有些位置可能存在多个数字,有些位置可能没有数字。当我们从头到尾扫描到下标为 i 的数字时,首先比较这个数字(用 m 表示)是不是等于 i ;如果是:则接着扫描下一个数据;如果不是,将他和第 m 个数据比较,若相等,则找到重复数字,若不相等,交换;继续遍历,直到找到重复数字;时间复杂度 O(n),空间复杂度 O(1)(尽管代码中有一个两重循环,但每个数字最多只要交换两次就能找到属于他的位置,因此总的时间复杂度是 O(n))
代码:
public static int help(int[] array){ int res = -1; if (array == null || array.length == 0){return res;} for (int i = 0; i < array.length; i++) { while (i != array[i]){ if (array[i] == array[array[i]]){ res = array[i]; break; } int temp = array[i]; array[i] = array[temp]; array[temp] = temp; } } return res; }
进阶题目:不修改数组找出数组中重复的数字
在一个长度为 n+1 的数组里的所有数字都在 1 ~ n 范围内,所以数组中至少有一个数字是重复的,请找出数组中任意一个重复数字,但不能修改数组。
分析:由于题目要求不能修改原数组,因此可以创建一个长度为 n+1 的辅助数组,然后逐一把原数组每个数字 m 复制到辅助数组中下标为 m 的位置,这样很容易就找到重复的数字,时间复杂度 O(n),空间复杂度 O(n)
进一步优化:空间复杂度O(1)
思路:假设数组中没有重复数字,那么 1 ~ n 范围里只有 n 个数字,现在数组里包含超过 n 个数字,所以一定包含了重复数字 ====>> 某范围里的数字个数对解决这个问题很重要
我们把 1 ~ n 的数字从中间的数字 m 分为两部分,前一部分为 1 ~ m,后一部分为 m + 1 ~ n;
若前半部分里数量 > m,前半部分一定包含重复数字;否则,后半部分一定包含重复数字;
继续把包含重复数字的区间一分为二,知道找到一个重复数字 ====>> 二分的思想 + 每一步统计区间里数量
栗子:以长度为 8 的数组 {2,3,5,4,3,2,6,7}为例,数组长度为 8,按题目要求,所有数字在 1 ~ 7 之间,取区间中间值 4 将区间分为两个子区间 1 ~ 4,4 ~ 7;
接下来我们统计原数组中介于 1 ~ 4之间的数出现的次数,分别是 2,3,4,3,2,次数是5次,5大于此区间长度4,因此此区间内的数在原数组中一定有重复;
同理,介于 4 ~ 7之间的数出现的次数,分别是 5,6,7,次数是3次;
接下里继续将1 ~ 4 区间一分为二,分别为 1 ~ 2,2 ~ 4;
统计原数组中介于 1 ~ 2之间的数出现的次数,分别是 2,2,次数是2次;
统计原数组中介于 2 ~ 4之间的数出现的次数,分别是 3,4,3,次数是3次,3大于此区间长度2,因此此区间内的数在原数组中一定有重复(要么3,要么4);
统计原数组中 3 和 4 出现的次数,发现 3 出现 2 次,找到重复数字。

浙公网安备 33010602011771号