1学想法 4学做法
方法1:类似于消除原理,既然某个数字大于长度的一半,那么我们就遍历数组,如果两个数不相等则消除,最后剩下的数就是我们要的。当然如果不存在这样的数,这是不行的。所以最后要再遍历一遍验证这个数的出现次数是否大于数组的一半。
具体实现:我们在考虑删除两个不同的数字的时候,实际上可以同过计数来实现,而不是物理上真正的删除。 在遍历数组的时候保存两个值:一个是数组中的一个数字,一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1。如果下一个数字和我们之前保存的数字不同,则次数减1。如果次数为零,我们需要保存下一个数字,并把次数设为1。由于我们要找的数字出现的次数比其他所有数字出现的次数之和还要多,那么要找的数字肯定是最后一次把次数设为1时对应的数字。
方法2:还有一种比较偷懒的方法,但效率要差,先排序qsort一下,那么中间那个元素一定是所求。
方法3:利用哈希表或者数组统计每个字符出现的次数,然后从里面找到出现次数超过一半的
方法4:如果一个数字才数组中出现的次数超过了数组长度的一半,那么对这个数组进行排序,位于数组中间位置的那个数就是出现次数超过一半的那个数。对数组排序的时间复杂度是O(nlog(n)),但是对于这道题目,还有更好的算法,能够在时间复杂度O(n)内求出。我们写过快速排序算法,其中的Partition()方法是一个最重要的方法,该方法返回一个index,能够保证index位置的数是已排序完成的,在index左边的数都比index所在的数小,在index右边的数都比index所在的数大。那么本题就可以利用这样的思路来解。
- 通过Partition()返回index,如果index==mid,那么就表明找到了数组的中位数;如果index<mid,表明中位数在[index+1,end]之间;如果index>mid,表明中位数在[start,index-1]之间。知道最后求得index==mid循环结束。
- 根据求得的index,遍历一遍数组,每当出现一个等于index所指向的数时time++,最后判断time是否大于数组长度的一半,如果大于则表明index所指向的数就是所求的数,如果不是,则表明不存在一个数出现的次数超过数组长度的一半。
参考来源:http://blog.163.com/xie_wenbin613/blog/static/175489095201242625851399/等
//代码用的方法1(假设一定存在此数字 否则输出时需要判断) #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> void fun(int array[],int arraySize) { int mark = array[0], count = 1, i; for(i = 1; i < arraySize; i++) { if( mark == array[i] ) { count++; } else { count--; if(count == 0) { mark = array[i]; count = 1; } } } printf("%d\n", mark); } int main() { int a1[] = {2,1,1,2,1,1,2,1,0,1,1,0}; fun(a1, sizeof(a1)/sizeof(a1[0])); return 0; }