主元素问题(判断数组是否出现主元素,O(n)时间内找出主元素,主元素出现次数)

来源:互联网 发布:别墅装修设计软件 编辑:程序博客网 时间:2024/05/18 20:37

          x称为一个长度为n的数组a的主元素,如果这个数组里面等于x的元素数目不少于n/2个。例如,a={2,3,2,2,5,3,2,4,2}, x = 2就是主元素。给定包含n个元素的数组a,主元素问题就是判断数组a是否包含一个主元素x。

         一个一般的方法就是对数组排序,然后相同的元素就会聚集在一起,这样的算法复杂度介于O(nlgn) 和O(n^2)之间。
        为了更快的找到主元素的解,就可以利用随机算法。随机挑选一个元素,然后判断它是否是主元素,如果是则返回true,否则返回false。

#include <stdio.h>#include <iostream>#include <math.h>#include <stdlib.h>using namespace std;bool IsItMajority( int *a, int n){int x, m, i;x = a[ rand() % n];m = 0;for( i = 0; i < n; i++){if( x == a[i]) m++;}if( 2 * m >= n)return true;elsereturn false;}//分析可知,每次运行的出错概率不到1/2,如果k此运行后,返回的答案仍然是false,则数组有主元素的概率不到1/2^k。//s代表容许错出的误差,则有 1/ 2^k < s 即: k > lg( 1/s ),因此IsItMajority(a, n)需要运行k = lg( 1/s)次,这就使得错误的概率小于s//例如:如果s = 0.01,那么k = lg( 1 / s ) = 7。这样只要IsItMajority(a, n)重复7次,我们就可以充分相信返回"false"的结果。bool RepeatedIsItMajority( int *a, int n, double s){int k = (int)log( 1/ s ) + 1;for( int i = 1; i <= k; i++ )if( IsItMajority( a, n ) ) return true;return false;}int main(int argc, char* argv[]){int a[] = { 2, 3, 2, 2, 5, 3, 2, 4, 2};int b[] = { 2, 3, 2, 2, 5, 3, 2, 4, 2, 3, 4, 5, 3, 4, 5};srand(time(0));if( RepeatedIsItMajority( a, sizeof(a) / sizeof( a[0] ), 0.001) )cout << "数组a包含一个主元素" << endl;elsecout << "数组a不包含一个主元素" << endl;if( RepeatedIsItMajority( b, sizeof(b) / sizeof( b[0] ), 0.001) )cout << "数组b包含一个主元素" << endl;elsecout << "数组b不包含一个主元素" << endl;return 0;}

题目:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。

分析:这是一道广为流传的面试题,包括百度、微软和Google在内的多家公司都曾经采用过这个题目。要几十分钟的时间里很好地解答这道题,除了较好的编程能力之外,还需要较快的反应和较强的逻辑思维能力。

看到这道题,我们马上就会想到,要是这个数组是排序的数组就好了。如果是排序的数组,那么我们只要遍历一次就可以统计出每个数字出现的次数,这样也就能找出符合要求的数字了。题目给出的数组没有说是排好序的,因此我们需要给它排序。排序的时间复杂度是O(nlogn),再加上遍历的时间复杂度O(n),因此总的复杂度是O(nlogn)

前面的思路没有考虑到题目中数组的特性:数组中有个数字出现的次数超过了数组长度的一半。也就是说,有个数字出现的次数比其他所有数字出现次数的和还要多。因此我们可以考虑在遍历数组的时候保存两个值:一个是数组中的一个数字,一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1。如果下一个数字和我们之前保存的数字不同,则次数减1。如果次数为零,我们需要保存下一个数字,并把次数设为1。由于我们要找的数字出现的次数比其他所有数字出现的次数之和还要多,那么要找的数字肯定是最后一次把次数设为1时对应的数字。

基于这个思路,我们不难写出如下代码:

#include <iostream>using namespace std;int MajorityElement( int *a, int n){int k, i;int elem;k = 1;elem = a[0];for( i = 1; i <n; i++ ){if( a[i] == elem ) {k++;}else{k--;if( k == 0 ) { k = 1; elem = a[i]; }}}//cout << k << endl;return elem;}int main(int argc, char* argv[]){int a[] = {2, 3, 2, 2, 5, 3, 2, 4, 2, 2};cout << "主元素是:" << MajorityElement( a, sizeof( a ) / sizeof( a[0] ) ) << endl;return 0;}

     基于以上思路,可以很容易得出数组中的主元素出现了几次,其他元素出现了几次。主元素的出现次数: (n  - k) / 2 + k


参考资料:http://zhedahht.blog.163.com/blog/static/25411174201085114733349/