c语言讨论寻找多数元素(主元素问题)

摘要

c语言讨论寻找多数元素(主元素问题),这个问题最初由投票问题引出,比如在投票系统中如果有一个人的票数超过50%,那么这个人立即当选。

下面我们将问题抽象出来。

一.问题描述

令A[1.....n]是一个整数序列,A中的整数a如果在A中出现的次数多于[n/2]次,那么a就称为多数元素。

比如现在有一个序列,已知其中有一个数出现的次数超过了50%,请你找出这个数。如3,3 ,1 ,1,3 ,2,3中,出现次数超过50%的数是3。

小伙伴们,你们有什么方法吗?

二.方法思路

我当时就想啊,第一个方法,两两比较呗,分别记录每个数字的出现的次数,2个for循环就解决了,代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    int n;
    int a[101];
    int count, max=0;
    printf("数据个数:\n");
    scanf_s("%d", &n);
    int i, j, k = 0;
    for (i = 1; i <= n; ++i)           //循环存入数据
    {
        scanf_s("%d", &a[i]);
    }
    for (i = 1; i <= n; ++i)           //遍历每一个数据
    {
        count = 0;
        for (j = 1; j <= n; ++j)
        {
            if (a[i] == a[j] && i != j)       //遇到相同的数据,计数器加1
            {
                count++;
            }
        }
        if (count > max)                //寻找有相同数据最多的数
        {
            max = count;
            k = a[i];
        }
    }
    printf("%d\n",k);
    return 0;
}

这种方法显然时间复杂度为O(n^ 2), 即n的平方。那么我有思考了,这样的处理还能更好吗?当然有,我脑中一机灵(哈哈我觉得可能是彼得一机灵,天哪,我为什么会想到这个),想到了排序,排完序后所有相同的数会就会在一起。然后再从头到尾扫一遍,用一个变量count来统计,并再用一个max来更新count的最大值。这段代码之所以比刚才那2个for循环的方法要好,是因为我们可以使用堆排或快排,这样时间复杂度降为O(n*log n),比刚才的O(n^ 2)要快很多。如果不用堆排或快排,则时间复杂度仍为O(n^2),代码如下:

快排:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int a[101], n;         //定义全局变量,因为这两个变量在子函数中使用


void quicksort(int left, int right)
{
    int i, j, t, temp;
    if (left > right)
    {
        return;
    }
    temp = a[left];
    i = left;
    j = right;
    while (i != j)
    {
        //顺序很重要,要先从右往左找
        while (a[j] >= temp && i < j)
        {
            j--;
        }
        //再从左往右找
        while (a[i] <= temp && i < j)
        {
            i++;
        }
        //交换两个数在数组中的位置
        if (i < j)//当i与j没有相遇时
        {
            t = a[i];
            a[i] = a[j];
            a[j] = t;
        }
    }
    //最后将基准数归位
    a[left] = a[i];
    a[i] = temp;

    quicksort(left, i - 1);
    quicksort(i + 1, right);
    return;
}


int main(void)
{
    int count = 1, max = 0;
    printf("数据个数:\n");
    scanf_s("%d", &n);
    int i, k = 0;
    for (i = 1; i <= n; ++i)           //循环存入数据
    {
        scanf_s("%d", &a[i]);
    }
    quicksort(1,n);                    //快速排序
    for (i = 1; i <= n; ++i)           //遍历每一个数据
    {
        if (a[i] == a[i + 1])
        {
            count++;
        }
        else
        {
            if (count > max)                //寻找有相同数据最多的数
            {
                max = count;
                k = a[i];
            }
            count = 1;
        }

    }
    printf("%d\n",k);
    return 0;
}

堆排:

/*对于堆,作向上调整,对内部变量h[101]作调整*/
int* siftup(int n, int h[101])
{
    int i = n, flag = 0, j;
    while (i >= 1 && flag == 0)
    {
        if (h[i] < h[i / 2] && i >= 1)
        {
            j = i / 2;
            int t;
            t = a[i];
            a[i] = a[j];
            a[j] = t;
            i = j;
        }
        else
        {
            flag = 1;
        }
    }
    return h;
}

上面的代码用于堆排序一个数组,排好后在主函数中给出第一个数,与第二个数比较,如相等count加1,如不相等,与max比较,更新max,最终找出多数元素。

/*对于堆,作向下调整,对内部变量h[101]作调整*/
int* siftdown2(int x, int num, int h[101])
{
    int n = num, flag = 0;
    int i, j;
    j = x;
    while (j <= n && flag == 0)
    {
        if (h[j] > h[2 * j] && 2 * j <= n)
        {
            i = 2 * j;
        }
        else
        {
            i = j;
        }
        if (h[i] > h[2 * j + 1] && 2 * j + 1 <= n)
        {
            i = 2 * j + 1;
        }
        if (i != j)
        {
            int t;
            t = a[i];
            a[i] = a[j];
            a[j] = t;
            j = i;
        }
        else
        {
            flag = 1;
        }
    }
    return h;
}


int main(void)
{
    int count = 1, max = 0;
    printf("数据个数:\n");
    scanf_s("%d", &n);
    int i, k = 0;
    for (i = 1; i <= n; ++i)           //循环存入数据
    {
        scanf_s("%d", &a[i]);
    }
    siftup(n, a);                        //堆排序
    int c = a[1]; 
    int num = n;
    for (i = 1; i <= n; ++i)           //遍历每一个数据
    {
        a[1] = a[num];
        --num;
        siftdown2(1, num, a);          //对于堆,作向下调整,对内部变量h[101]作调整
        if (a[1] ==c)                  //找出下一个较小的数,与前一个比较是否相等
        {
            count++;
        }
        else
        {
            if (count > max)                //寻找有相同数据最多的数
            {
                max = count;
                k = a[i];
            }
            count = 1;
        }
        c = a[1];
    }
    printf("%d\n",k);
    return 0;
}

然后想到这,我再一此灵机一动,如果这个数出现的次数大于50%的话,排序之后应该就是位于n/2的位置的那个数,故如果堆排或快排,直接排完序输出n/2位置的那个数就是这串序列的多数元素,时间复杂度变为O(log n)。

三.深入思考

刚才我们的问题是已知这个序列存在一个数出现的次数大于50%,那如果没有一个数出现的次数大于50%,那我们找到的数就一定是错的,那有人就说了,将找到的数在与序列对比一遍,查看它出现的次数是否超过50%即可,当然可以,那么我们上面所有的代码时间复杂度都要再乘以n,时间过于复杂,那么在一个数列中不知道是否存在一个数,它出现的次数大于50%的前提下,如何找到这个数,或验证它不存在呢?

哈哈,新的问题诞生了!!!

我又开始了一大波思考,,,
这里提供一个思路,我开始是这样想的,可以用一个数组来记录每个数出现的次数,就是桶排序那样,数组中的最大值的下标就是出现次数超过50%的数。时间空间复杂度是O(n+m) ,但是如果数的大小分布跨度很大怎么办,比如1,10000,10000000·····这样就很浪费空间。嗯~,补充一下吧,可以将数据离散化一下,再开一个数组一一对应,让1对应1,2对应10000,3对应10000000·····。

听到这你一定晕了吧,哈哈,这只是一个思路,下面我们来点硬货,小编查阅了大量资料(显得我好爱学习呀!!你们可以当我不存在,啊,我飘了),发现这是著名的“多数元素问题”,也叫“主元素问题”。

四. 终结问题(探索主元素问题)

如果一个序列存在一个数,且它出现的次数大于50%,则这个序列有一个特性:在原序列中去除两个不同的元素后,那么在原序列中出现次数超过了50%的数,在新序列中的出现次数也一定会超过50%。

知道了这个特性问题应该很好解决了吧!!

不!我在探索的过程中发现了另一个问题,我们来看看吧。

例:
序列 1,4,4,7,4,1这个序列显然4是多数元素。
去除1, 4 仍然4是多数元素。
去除1, 7更是。

序列1 ,4,4, 7, 4 ,7,7这个序列显然没有多数元素。
但是如去除1, 4,则序列变为4, 7, 4, 7 ,7,这时 7为多数元素。

故产生了一个新问题,在最终序列遍历完后找到的多数元素不一定是多数元素,但如果序列有多数元素存在,则在最终序列遍历完后找到的多数元素一定为该序列的多数元素,故我们需要一个函数来找序列中是否有多数元素

话不多说上代码,先找这个多数元素:

/*找出多数元素*/
int Candidate(int m)
{
    int c=a[m]; //将新序列第一个数开始向后遍历
    int count = 1;//定义计数器
    int j;
    for ( j= m + 1; count > 0 && j < n; ++j) //如果不相等的数等于了相等的数,去除该段已遍历的序列
    {
        if (a[j] == c)//如果与第一个数相等,计数器加1
        {
            count++;
        }
        else
        {
            count--;       //不相等减1
        }
    }
    if (j == n)           //序列遍历完
    {
        return c;
    }
    else                 //如没有,则从接下来新的数开始再往后遍历
    {
        return Candidate(j);
    }
}

就上面所说我们找到这个元素后,要判断这个数是否出现次数大于50%,故我们需要一个函数来判断序列中是否有多数元素,代码如下:

/*判断是否有多数元素*/
int Majority()
{
    int c = Candidate(1);
    int i, count = 0;
    for (i = 1; i <= n; ++i)
    {
        if (a[i] == c)
        {
            count++;
        }
    }
    if (count >= n / 2)              //如超过一半,则存在多数元素,且为c这个变量中所存的数
    {
        return c;
    }
    else
    {
        return 0;
    }
}

这个算法将所有数读了一遍,故时间复杂度最优为O(n) 。下面展示完整代码:

#include <stdio.h>                    /*寻找多数元素(主元素)问题*/
#include <stdlib.h>                   /*此代码为最简代码(时间复杂度最简)*/


int a[101], n;                        //定义全局变量,因为这两个变量在子函数中使用



/*找出多数元素*/
int Candidate(int m)
{
    int c=a[m]; //将新序列第一个数开始向后遍历
    int count = 1;//定义计数器
    int j;
    for ( j= m + 1; count > 0 && j < n; ++j)   //如果不相等的数等于了相等的数,去除该段已遍历的序列
    {
        if (a[j] == c)//如果与第一个数相等,计数器加1
        {
            count++;
        }
        else
        {
            count--;      //不相等减1
        }
    }
    if (j == n)           //序列遍历完
    {
        return c;
    }
    else                  //如没有,则从接下来新的数开始再往后遍历
    {
        return Candidate(j);
    }
}


/*判断是否有多数元素*/
int Majority()
{
    int c = Candidate(1);
    int i, count = 0;
    for (i = 1; i <= n; ++i)
    {
        if (a[i] == c)
        {
            count++;
        }
    }
    if (count > n / 2)          //如超过一半,则存在多数元素,且为c这个变量中所存的数
    {
        return c;           
    }
    else
    {
        return 0;
    }
}



int main(void)
{
    printf("数据个数:\n");
    scanf_s("%d",&n);
    int i;
    for (i = 1; i <= n; ++i)
    {
        scanf_s("%d",&a[i]);
    }
    if (i = Majority() != 0)
    {
        printf("%d", i);
    }
    else
    {
        printf("不存在多数元素\n");
    }
    return 0;
}

好了,这就是本次小编为大家带来的关于c语言讨论寻找多数元素(主元素问题)的全部内容啦!感谢您的阅读,分享知识,这个世界还能更好!

参考资料

  1. 《C语言程序设计(第5版)》 谭浩强 著 清华大学出版社

  2. 《C语言程序设计教程 》 王曙燕 主编 人民邮电出版社

  3. 《啊哈!算法》 啊哈磊 著 人民邮电出版社

  4. 《数据结构与算法分析C语言描述》 [美] 马克·艾伦·维斯 著 机械工业出版社

全部评论

相关推荐

10-17 12:16
同济大学 Java
7182oat:快快放弃了然后发给我,然后让我也泡他七天最后再拒掉,狠狠羞辱他一把😋
点赞 评论 收藏
分享
躺尸修仙中:因为很多92的也去卷中小厂,反正投递简历不要钱,面试不要钱,时间冲突就推,不冲突就面试积累经验
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
昨天 10:52
点赞 评论 收藏
分享
评论
点赞
1
分享
牛客网
牛客企业服务