查找数组中重复的数字(二)

需求

不修改数组找出重复的数字。
在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,单不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,6,2,6,7},那么对应的输出是重复的数字2或者6。(不要求找出所有重复数字,只要找出其中之一即可)

 

分析

由于不能修改输入的数组,因此不能在原始数组中做排序再判断下标。需要考虑其他的方法。

思路一:

使用一个辅助的数组,长度为n,如:tmp[n],然后开始遍历原始数组
1.如果数组中出现数字x,就判断数组下标为x的节点是否还是初始值0,即tmp[x]是否为0;
2.如果tmp[x] == 0,就标记为1,然后继续遍原始数组的下一个元素,继续步骤1;
3.如果tmp[x] != 0,说明之前已经表标记为1了,即之前已经出现过这个相同的元素,即已经找到重复数字了。
见示例代码中的findRepeatNumberWithExArray

 

思路二:

暂且称之为分段法。

假如数组如下{2,3,5,4,6,2,6,7}
数组的取值范围在1~7,数组的长度为8,所以在1~7里面肯定是有1个或者1个以上重复的数字。
1.把1~7的取值范围分成两部分,前半部分为1~4,后半部分是5~7;
2.计算1~4的数字有多少个,从此例子来看,一共有4个(2,3,4,2),1~4之间范围为4,数组的长度为4,有可能有重复的数字也有可能没有重复的数字;
3.由于剩下的数字范围是5~7范围为3,数组长度是4(总长度8减去前半部分数字的长度,就是后半部分剩余的长度4),因此肯定有重复数字;
4.查找范围变成5~7,重复步骤1,把取值范围分成两部分,前半部分是5~6,后半部分是7;接着继续步骤二;
...
最后.当查找的数字范围变成1,并且计算得出的数字个数大于1,说明这个数字就是重复的数字,任务结束。
见实例代码中的findRepeatNumberBySegment

 

c++示例代码如下:

  1 #include <iostream>
  2 
  3 using namespace std;
  4 
  5 const int ARR_LENGTH = 8;
  6 
  7 /************************************************************************/
  8 /* @brief 使用额外的辅助数组查找数组中其中一个重复数字
  9 /* @param arr数组
 10 /* @param length数组长度
 11 /* @return 数组中其中一个重复的数字,-1表示没找到
 12 /************************************************************************/
 13 int findRepeatNumberWithExArray(const 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     //一个临时的辅助数组
 24     int *tmpArr = new int[length];
 25     memset(tmpArr, 0, sizeof(int)*length);
 26 
 27     for (int i = 0; i < length; ++i)
 28     {
 29         if (tmpArr[arr[i]] != 0)
 30         {
 31             result = arr[i];
 32             break;
 33         }
 34         else
 35         {
 36             tmpArr[arr[i]] = 1;
 37         }
 38     }
 39     delete[] tmpArr;
 40     tmpArr = nullptr;
 41     return result;
 42 }
 43 
 44 /************************************************************************/
 45 /* @brief 计算数组中在某一个范围内的数字的数量
 46 /* @param arr数组
 47 /* @param length数组长度
 48 /* @param start范围开始位置(包括start)
 49 /* @param end范围结束位置(包括end)
 50 /* @return 在范围内的数字的数量
 51 /************************************************************************/
 52 int countRange(const int* arr, const int length, const int start, const int end)
 53 {
 54     int counter = 0;
 55     if (!arr || length < 1 || end < start)
 56     {
 57         return counter;
 58     }
 59 
 60     for (int i = 0; i < length; ++i)
 61     {
 62         //如果数字在范围内则把个数加1
 63         if (arr[i] >= start && arr[i] <= end)
 64         {
 65             counter++;
 66         }
 67     }
 68     return counter;
 69 }
 70 
 71 /************************************************************************/
 72 /* @brief 使用分段法查找数组中其中一个重复数字
 73 /* @param arr数组
 74 /* @param length数组长度
 75 /* @return 数组中其中一个重复的数字,-1表示没找到
 76 /************************************************************************/
 77 int findRepeatNumberBySegment(const int* arr, const int length)
 78 {
 79     int result = -1;
 80 
 81     //入参有问题直接返回,少于2个数字肯定没有重复的
 82     if (!arr || length < 2)
 83     {
 84         return result;
 85     }
 86 
 87     int start = 1;
 88     int end = length - 1;
 89     int counter = 0;
 90 
 91     while (start <= end)
 92     {
 93         int middle = (end - start) / 2 + start;
 94         //计算前半部分的数字的数量
 95         counter = countRange(arr, length, start, middle);
 96         //只有一个数字并且数量大于1,说明找到了重复的数字了
 97         if (start == middle && counter > 1)
 98         {
 99             result = start;
100             break;
101         }
102         
103         //假如前半部分数字数量少于等于范围,说明这个范围可能有重复的也有可能没有重复的数字,但是后一半的数字肯定有重复的,直接在后一半查找
104         if (counter <= (middle - start + 1))
105         {
106             start = middle + 1;
107         }
108         //假如前半部分数字的数量大于数字范围,说明这个范围内肯定有重复的数字,继续查找
109         else
110         {
111             end = middle;
112         }
113     }
114     return result;
115 }
116 
117 int main()
118 {
119     int arr[ARR_LENGTH] = {2,3,5,4,6,2,6,7};
120 
121     cout << "原始数据:" << endl;
122 
123     for (int i = 0; i < sizeof(arr) / sizeof(int); ++i)
124     {
125         cout << arr[i] << "  ";
126     }
127 
128     int repeat = findRepeatNumberWithExArray(arr, ARR_LENGTH);
129     cout << "\n\n辅助数组法查找重复数字:" << repeat << endl;
130 
131     repeat = findRepeatNumberBySegment(arr, ARR_LENGTH);
132     cout << "\n\n分段法查找重复数字:" << repeat << endl;
133 
134     return 0;
135 }

 

运行结果:

 

posted @ 2019-07-10 18:05  纯情小浩浩  阅读(352)  评论(0编辑  收藏