需求
找出数组中重复的数字。
在一个长度为n的数组里的所有数字都在0~n-1的范围内,数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。(不要求输出所有的重复数字,找到其中之一即可)
分析
思路一:
对输入的数组先进行排序,然后从头到位遍历排序好的数组,当出现第一个数组下标与存储的值不一样的(array[i] != i),就说明找到了重复数字。排序需要的时间复杂度为O(nlgn)。见示例代码中的findRepeatNumberWithSort。
思路二:
使用一个辅助的数组,长度为n,如:tmp[n],然后开始遍历原始数组
1.如果数组中出现数字x,就判断数组下标为x的节点是否还是初始值0,即tmp[x]是否为0;
2.如果tmp[x] == 0,就标记为1,然后继续遍原始数组的下一个元素,继续步骤1;
3.如果tmp[x] != 0,说明之前已经表标记为1了,即之前已经出现过这个相同的元素,即已经找到重复数字了。
这种方法由于需要使用一个长度为n的辅助数组,空间复杂度为O(n)。见示例代码中的findRepeatNumberWithExArray。
思路三:
不使用辅助数组,也不用提前排序的方法,暂且称为动态排序法吧。
先考虑另外一种情况:
假如一个排序好的长度为n的数组,数组中的数字为0~n-1,并且没有重复数字,那这个数组中存储的数字应该是:array[0]=0,array[1]=1,array[2]=2....array[n-1]=n-1。也就是说数组的下标应该是跟存储的值是一样的。
回到这个需求:
如果数组中的数字有重复,说明肯定存在array[i]=i并且array[j]=i(可能还有array[k]=i,array[l]=i.....)。
大前提了解了,下面开始按照这个思路查找重复数字。
从原始数组第0个位置开始遍历,
1.假如 array[i] == i 说明,这个位置放置了对的数值,然后开始查检查array[i+1];
2.假如 array[i] != i ,假如值为x,判断array[x]是不是等于x,如果是说明array[i]和array[x]都等于x,查找到了2个x,说明任务结束,可以返回结果了;
3.假如 array[i] != i ,假如值为x,判断array[x]是不是等于x,如果不是,就把array[i]跟array[x]的数字交换,使得array[x]=x,这样x就放到了array[x]的位置上,也就是找到对应的位置了,然后继续步骤1,判断当前的array[i]的数字。
通过这种思路,可以看到不需要提前对数组进行排序,只需要在判断的时候顺便排序即可,只需一个循环,时间复杂度是O(n),由于只需要使用一个辅助的空间用于交换数据,空间复杂度是O(1)【ps:甚至可以不用辅助空间来交换两个数字】。
见示例代码中的findRepeatNumberDynamicSort。
c++示例代码如下:
1 #include <iostream> 2 #include <algorithm> 3 4 using namespace std; 5 const int ARR_LENGTH = 7; 6 7 /************************************************************************/ 8 /* @brief 使用sort查找数组中其中一个重复数字 9 /* @param arr数组 10 /* @param length数组长度 11 /* @return 数组中其中一个重复的数字,-1表示没找到 12 /************************************************************************/ 13 int findRepeatNumberWithSort(int* arr, const int length) 14 { 15 int result = -1; 16 17 //入参有问题直接返回,少于2个数字肯定没有重复的 18 if (!arr || length < 2) 19 { 20 return result; 21 } 22 23 sort(arr, arr + length); 24 for (int i = 0; i < length; ++i) 25 { 26 if (arr[i] != i) 27 { 28 result = arr[i]; 29 break; 30 } 31 } 32 return result; 33 } 34 35 /************************************************************************/ 36 /* @brief 使用额外的辅助数组查找数组中其中一个重复数字 37 /* @param arr数组 38 /* @param length数组长度 39 /* @return 数组中其中一个重复的数字,-1表示没找到 40 /************************************************************************/ 41 int findRepeatNumberWithExArray(int* arr, const int length) 42 { 43 int result = -1; 44 45 //入参有问题直接返回,少于2个数字肯定没有重复的 46 if (!arr || length < 2) 47 { 48 return result; 49 } 50 51 //一个临时的辅助数组 52 int *tmpArr = new int[length]; 53 memset(tmpArr, 0, sizeof(int)*length); 54 55 for (int i = 0; i < length; ++i) 56 { 57 if (tmpArr[arr[i]] != 0) 58 { 59 result = arr[i]; 60 break; 61 } 62 else 63 { 64 tmpArr[arr[i]] = 1; 65 } 66 } 67 delete[] tmpArr; 68 tmpArr = nullptr; 69 return result; 70 } 71 72 /************************************************************************/ 73 /* @brief 不使用辅助数组,动态排序法查找重复数字 74 /* @param arr数组 75 /* @param length数组长度 76 /* @return 数组中其中一个重复的数字,-1表示没找到 77 /************************************************************************/ 78 int findRepeatNumberDynamicSort(int* arr, const int length) 79 { 80 int result = -1; 81 82 //入参有问题直接返回,少于2个数字肯定没有重复的 83 if (!arr || length < 2) 84 { 85 return result; 86 } 87 88 int i = 0; 89 while (i < length) 90 { 91 int key = arr[i]; 92 //如果数组下标和存储的值相同(arr[i]等于i),说明这个值已经放到了它应该在的地方(即已经排序到了正确的位置),继续数组中的下一个值 93 if (key == i) 94 { 95 ++i; 96 } 97 else 98 { 99 //由于key跟i不相同,并且arr[key],arr[i]这两个位置都存储了同一个值key,说明找到了相同的数字,结束循环,返回结果 100 if (arr[key] == key) 101 { 102 result = key; 103 break; 104 } 105 //让arr[i]和arr[key]的值交换,使得arr[key]这个位置存放他该放的值,即arr[key]等于key 106 else 107 { 108 arr[i] = arr[key]; 109 arr[key] = key; 110 } 111 } 112 } 113 return result; 114 } 115 116 int main() 117 { 118 int arr[ARR_LENGTH] = { 2,3,1,0,2,5,3 }; 119 cout << "原始数据:" << endl; 120 121 for (int i = 0; i < sizeof(arr) / sizeof(int); ++i) 122 { 123 cout << arr[i] << " "; 124 } 125 126 int repeat = findRepeatNumberWithSort(arr, ARR_LENGTH); 127 cout << " 排序法查找重复数字:" << repeat << endl; 128 129 repeat = findRepeatNumberWithExArray(arr, ARR_LENGTH); 130 cout << " 辅助数组法查找重复数字:" << repeat << endl; 131 132 repeat = findRepeatNumberDynamicSort(arr, ARR_LENGTH); 133 cout << " 动态排序法查找重复数字:" << repeat << endl; 134 135 return 0; 136 }