面试题3:数组中重复的数字(Java 实现)
题目一:在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。
方法一:时间复杂度为O(n*nlogn),空间复杂度为O(1)
先把输入的数组排序,排序一个长度为n的数组需要 O(nlogn)的时间,然后只需从头到尾扫描一次数组,需要 O(n)的时间,比较 a[i] 是否等于 a[i+1],若相等,则说明有重复的元素,输出 a[i] ,若不相等,说明数组中没有重复的元素。
方法二:时间复杂度O(n),空间复杂度O(n),以空间换时间
利用一个大小为O(n)的哈希表来解决问题,首先从头到尾扫描一次数组,时间复杂度为 O(n),每扫描一个元素都可以用 O(1)的时间来判断哈希表中有没有这个元素,若哈希表中没有这个元素,则把它加入到哈希表中,若哈希表中存在这个元素,说明这个元素重复了,就找到了一个重复的元素。
方法三:时间复杂度O(n),空间复杂度O(1)
首先从头到尾扫描数组,时间复杂度为 O(n),依次判断数组中的元素与其下标是否对应相等。当不相等时:分为两种情况,如果这个元素与以这个元素值为下标的元素相等(元素本应该在的位置),则说明这个元素重复了;若不相等,交换元素,把元素放到与其下标对应的位置。以数组{2,3,1,0,2,5,3,}为例,第0个元素(下标为0)值为2,与下标不对应相等,则在比较下标为2所在的元素,这里是1,不相等,即交换2和1,把2放回与下标对应的位置。
测试用例:
- 功能测试:长度为n的数组中包含一个或多个重复的数字。
- 边界测试:数组中不包含重复的数字
- 负面测试:输入空指针:长度为n的数组包含0——n-1之外的数字
import java.util.HashMap; public class test_three { //方法二:利用一个大小为O(n)的哈希表 public boolean duplicate(int numbers[],int length,int [] duplication) { boolean flag = false; if(numbers==null || length==0){ return flag; } HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); for(int num: numbers){ if(map.containsKey(num)){ flag = true; duplication[0] = num; break; } map.put(num, 0); } return flag; } /* 方法三:时间复杂度O(n),空间复杂度O(1) 最优解法:虽然代码中有一个二重循环,但每个数字最多只要交换两次就能找到属于它自己的 位置,因此总的时间复杂度是O(n) */ public boolean duplication(int[] numbers, int length, int[] duplication){ //如果输入的数组为空,直接返回false if(numbers == null || length<=0)return false; //从头到尾扫描数组 for(int i=0; i<length; i++){ //如果数组不在1——n-1的范围内,直接返回false if(numbers[i]<0 || numbers[i]>=length)return false; //当数组中某个元素不等于其下标时,分为两种情况 while(numbers[i]!=i){ //如果这个元素与以这个元素值为下标的元素相等(元素本应该在的位置),则说明这个元素重复了 if(numbers[i] == numbers[numbers[i]]){ //把这个重复的元素存储在duplication[]数组中 duplication[0] = numbers[i]; return true; }else{ //若不相等,即交换,把元素放到与下标对应的位置 int temp = numbers[i]; numbers[i] = numbers[temp]; numbers[temp] = temp; } } } return false; } }
题目二:
在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但是不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。
分析1: 时间复杂度O(n),空间复杂度O(n)
由于不能修改输入的数组,我们可以创建一个长度为n+1的辅助数组,然后逐一把原数组的每个数字复制到辅助数组。如果原数组中被复制的数字是m,则把它复制到辅助数组中下标为m的位置。如果下标为m的位置上已经有数字了,则说明该数字重复了。由于使用了辅助空间,故该方案的空间复杂度是O(n) 。
public class Solution { public static int getDuplication(int[] arr) { if (arr == null || arr.length == 0) { return -1; } for (int i = 0; i < arr.length; i++) { if (arr[i] < 1 || arr[i] >= arr.length) { return -1; } } int[] tempArr = new int[arr.length]; for (int i = 0; i < arr.length; i++) { if (arr[i] == tempArr[arr[i]]) { return arr[i]; } tempArr[arr[i]] = arr[i]; } return -1; } public static void main(String[] args) { int[] numbers = { 2, 1, 5, 4, 3, 2, 6, 7 }; System.out.println(getDuplication(numbers)); } }
分析二: 时间复杂度O(nlogn),空间复杂度O(1)
由于分析一的空间复杂度是O(n),因此我们需要想办法避免使用辅助空间。我们可以这样想:如果数组中有重复的数,那么n+1个1~n范围内的数中,一定有几个数的个数大于1。那么,我们可以利用这个思路解决该问题。
利用二分查找算法,我们把从1–n的数字从中间的数字m分为两部分,前面一半为1–m,后面一半为m+1–n。如果1–m的数字的数目等于m,则不能直接判断这一半区间是否包含重复的数字,反之,如果大于m,那么这一半的区间一定包含重复的数字;如果小于m,另一半m+1–n的区间里一定包含重复的数字。接下来,我们可以继续把包含重复的数字的区间一分为二,直到找到一个重复的数字。
这个方法不能保证找到所有重复的元素。
public class Solution { public static int getDuplication(int[] arr) { if (arr == null || arr.length == 0) { return -1; } for (int i = 0; i < arr.length; i++) { if (arr[i] < 1 || arr[i] >= arr.length) { return -1; } } int start = 1; int end = arr.length - 1; int mid = 0; int count = 0; while (start <= end) { if (start == end) { count = countRange(arr, start, end); if (count > 1) { return start; } else { break; } } mid = (start + end) / 2; count = countRange(arr, start, mid); if (count > mid - start + 1) { end = mid; } else { start = mid + 1; } } return -1; } public static int countRange(int[] arr, int start, int end) { int count = 0; for (int i = 0; i < arr.length; i++) { if (arr[i] >= start && arr[i] <= end) { count++; } } return count; } public static void main(String[] args) { int[] numbers = { 1, 3, 5, 4, 2, 5, 6, 7 }; int result = Solution.getDuplication(numbers); System.out.println(result); } }
- <剑指offer 面试题3> 数组中重复的数字(Java实现)
- 数组中重复的数字java实现
- 剑指offer面试题3-数组中重复的数字 java
- 剑指offer--面试题8:旋转数组的最小数字--Java实现
- Java实现找出数组中重复的数字
- 剑指offer编程题Java实现——面试题8旋转数组的最小数字
- 剑指Offer面试题40:数组中只出现一次的数字 Java实现
- 剑指Offer面试题51(Java版):数组中重复的数字
- 剑指offe面试题8 旋转数组的最小数字 (java实现)
- 剑指Offer面试题38:数字在排序数组中出现的次数 Java实现
- java 实现递归查找数组中的重复数字
- java实现list清除重复的字符串;arraylist动态数组;角谷定理最终得1,找出10000以内哪个数字的步骤最长
- Java实现-删除排序数组中的重复数字2
- java 实现二分法查找出数组重复数字
- 剑指Offer面试题29:数组中出现次数超过一半的数字 Java实现
- 数据结构与算法分析笔记与总结(java实现)--数组8:数组中重复的数字
- Java实现-删除排序数组中的重复数字1
- 剑指offer面试题11:旋转数组的最小数字(Java 实现)
- java实现二分法查找出数组重复数字
- 数组中重复的数字(Java实现)