摩尔投票算法

现在有一组数,某一个数字a重复的次数超过了总数的一半,找出这个数字

对于这种情况,可以采用摩尔投票算法

思路就是:既然它出现的次数大于n/2 ,那我每次拿出两个不相等的数,直到拿完或者剩下的数字都一样,

最坏的情况就是每次拿出的数字都包含a,即使这样,最后数组里的数字仍然都是a

可以这样证明,比如有x个非a数字,有y个a

x+y=n;y>n/2  =>  y>x  =>  y-x>0

 

可以这样去实现它:

就假设第一个数字是主要元素,然后开始遍历这个数组

遇到相同的我就cnt++ ,遇到不同的我就cnt --

每当cnt等于0,我就又回到了最开始的情况,那么怎么保证剩下的数组里的a仍然超过一半呢

示意图是这样的: [A0.... Z0,A1...Z1], 刚好走到A1的时候cnt=0

只要保证A0-Z0段a的个数没有超过一半,就可以保证A1-Z1段a的个数仍然超过一半

因为 y0+y1=y; num0+num1 =num ;2*y>num;   =>  2*y0+2*y1>num0+num1   =>  2*y1>num1+(num0-2*y0)

所以当A0-Z0段a的个数小于等于该段总数一半的时候,A1-Z1段a的个数就是大于该段总数一半的

实质上A0-Z0段a的个数就是小于该段总数一半,因为倘若超过该段一半,cnt一定是个正数,不会为零

那么就又回到了原来的情况,周而复始即可

 

 当遍历完成,如果cnt大于0,应该就意味着存在主要的数字,但是可能刚好上面说的A2就是最后一个数字,那么此时cnt也为1

所以稳妥起见,还是再遍历一遍,统计下该数字出现的次数

 

写个程序测试一下:

 

#include<stdio.h>

int getMajor(int * nums,int num_size) {
    int major, cnt = 0;
    for (int i = 0; i < num_size;i++) {
        if (cnt == 0) {
            major = nums[i];
            cnt += 1;
        }
        else cnt += ((major == nums[i]) ? 1 : -1);
    }
    int cnt2 = 0;
    for (int i = 0; i < num_size; i++) {
        if (nums[i] == major) cnt2++;
        if ((cnt2 * 2) > num_size) return major;
    }
    return -1;
}
int main() {
    int arr[] = { 1,1,1,3,5 };
    printf("%d", getMajor(arr, 5));
    return 0;
}

 

posted @ 2021-02-20 01:11  田埂  阅读(72)  评论(0编辑  收藏  举报