需求
找出数组中重复的数字。
在一个长度为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 << "\n\n排序法查找重复数字:" << repeat << endl;
128
129 repeat = findRepeatNumberWithExArray(arr, ARR_LENGTH);
130 cout << "\n辅助数组法查找重复数字:" << repeat << endl;
131
132 repeat = findRepeatNumberDynamicSort(arr, ARR_LENGTH);
133 cout << "\n动态排序法查找重复数字:" << repeat << endl;
134
135 return 0;
136 }
运行结果: