找出数组中唯一的重复元素

来源:互联网 发布:淘宝优惠券源码 编辑:程序博客网 时间:2024/05/01 08:26

题目:数组 arr[N],1 至 N 这 N - 1 个数存放在 arr[N] 中,其中某个数重复一次,写一个函数,找出重复的数字。要求每个数组元素只能访问一次,不用辅助存储空间。
分析:由于题目要求每个数组元素只能访问一次,不用辅助存储空间,可以从原理上入手,采用数学求和法,因为只有一个数字重复一次,而数又是连续的,根据累加和原理,对数组的所有项求和,然后减去 1 至 N - 1 的和,即为所求的重复数。

#include <iostream>#define SUM(x) (x*(x+1)/2)int findData(int arr[], int N){    int sum = 0;    for (int i = 0; i < N; i++)        sum += arr[i];    int sum2 = SUM((N-1)); // 此处的N-1,一定要用小括号括起来。    return (sum - sum2);}int main(int argc, const char * argv[]) {    int arr[] = {1,2,5,6,2,3,4,7};    int len = sizeof(arr)/sizeof(int);    printf("唯一重复的元素是 %d\n", findData(arr, len));    return 0;}

拓展:如果题目没有要求每个数组元素只能访问一次,并且也可以使用辅助空间,还可以使用以下方法来求解。
(1)异或法
根据异或法的计算方式,每两个相异的数执行异或运算之后,结果为1;每两个相同的数异或之后,结果为0,所以数组 arr[N] 中的 N 个数异或结果 与 1 至 N - 1 异或的结果再做异或,得到的值即为所求。
设重复数为 A,其余 N - 2 个数异或结果为 B,N 个数异或结果为 A^A^B,1 至 N - 1 异或结果为 A^B,由于异或满足交换律和结合律,且 X^X = 0,0^X = X,则有 (A^B)^(A^A^B) = A^B^B = A。

(2)Hash法
<1> 首先,数组元素的取值范围是 1 ~ N - 1,其中最大的数值为 N - 1,所以要申请一个长度为 N 的新数组(即按照集合中最大元素max创建一个长度为max+1的新数组),并将新数组元素全部初始化为0。
<2> 然后从头开始遍历原数组arr[N],取每个数组元素 arr[ i ] 的值,将新数组中“以该值作为下标的元素“置1,如果已经置过1了,那么该数就是重复的数。空间复杂度为O(N)。

备注:Hash法 和 位图法(bitmap) 的比较
<1> 上述这种给新数组“初始化时置零、其后置一“的做法,类似于位图的处理方法,所以有人也称上述方法为--位图法。
<2> 所谓bitmap就是用一个bit位来标记某个元素对应的value,而key即是这个元素。由于采用bit为单位来存储数据,因此在可以大大的节省存储空间。
<3> 位图法,由于每个数都是互斥的,所以它本身就是一个最好的散列(即使用的也是hash映射),所以位图法也可算作hash算法。
<4> 位图通过使用 位数组 来表示某些元素是否存在,位数组中的元素取值非0即1,没有其他可能,而且真正使用位图法时,一定会涉及到位运算,如’ | ’ 、’ & ‘和‘<< ’ 等位运算。
<5> 如果只是使用了位运算中“非0即1“的思想来判重,而没有使用“位数组 + 位运算“,那么我认为称呼这类方法为hash法更合适一些,不应该称为位图法。
<6> 因为需要用到位运算,所以位图法一般只适用于“非重复的正整数的排序“、查找、判重、删除。

0 0