海量数据查找唯一数据问题
常见的问题有以下几类:
问题一:有101个数,为[1,100]之间的数,其中一个数是重复的,如何寻找这个重复的数,其时间复杂度和空间复杂度是多少?
方法:利用和
sum1=1+2+3+.....99;
sum2=a[0]+a[1]+.....a[99];
sum2-sum1=重复的那个值
int OnlyOneRepeate(int *iArray,int length) { int i,sum = 0,sumMax = 0; for(i = 0;i < length; i++) { sum += i; sumMax += iArray[i]; } return sumMax - sum; }
另外的方法:我们可以建立一个长度为length大小的数组,并且初始化为0:
int *nCount = new int [length]; nCount[ ] = {0}; for(int i = 0;i < length; i++) { nCount [iArray[ i ] ] ++; if(2 == nCount[ iArray[ i ] ]) return iArray[i]; }
此算法就是申请了一个大小为:length*4字节的空间,在一定程度上空间复杂度提高了;但是,在时间复杂度上有所降低。
问题二:有大量的数据,其中只有一个数字出现了一次,其他的数字至少出现了两次,如何找出这个只出现一次的数字,要求用最小时间复杂度和空间复杂度。
方法:利用a^b^b=a的原理
直接 a[0]^a[1]^.....a[99] = 只出现一次的那个值.
问题三:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。
数组中有个数字出现的次数超过了数组长度的一半。也就是说,有个数字出现的次数比其他所有数字出现次数的和还要多。因此我们可以考虑在遍历数组的时候保存两个值:一个是数组中的一个数字,一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1。如果下一个数字和我们之前保存的数字不同,则次数减1。如果次数为零,我们需要保存下一个数字,并把次数设为1。由于我们要找的数字出现的次数比其他所有数字出现的次数之和还要多,那么要找的数字肯定是最后一次把次数设为1时对应的数字。
基于这个思路,我们不难写出如下代码: bool g_bInputInvalid = false; ////////////////////////////////////////////////////////////////////////// // Input: an array with "length" numbers. A number in the array // appear more than "length / 2 + 1" times. // Output: If the input is valid, return the number appearing more than // "length / 2 + 1" times. Otherwise, return 0 and set flag g_bInputInvalid // to be true. ////////////////////////////////////////////////////////////////////////// int MoreThanHalfNum(int* numbers, unsigned int length) { if(numbers == NULL && length == 0) { g_bInputInvalid = true; return 0; } g_bInputInvalid = false; int result = numbers[0]; int times = 1; for(int i = 1; i < length; ++i) { if(times == 0) { result = numbers[i]; times = 1; } else if(numbers[i] == result) times++; else times--; } // verify whether the input is valid times = 0; for(int i = 0; i < length; ++i) { if(numbers[i] == result) times++; } if(times * 2 <= length) { g_bInputInvalid = true; result = 0; } return result; }
在上述代码中,有两点值得讨论:
(1)我们需要考虑当输入的数组或者长度无效时,如何告诉函数的调用者输入无效。关于处理无效输入的几种常用方法;
(2)本算法的前提是输入的数组中的确包含一个出现次数超过数组长度一半的数字。如果数组中并不包含这么一个数字,那么输入也是无效的。因此在函数结束前我还加了一段代码来验证输入是不是有效的。