有趣的问题系列-主元素问题

什么是主元素问题?

给定一个有nn个元素的数列,保证其中有一个数出现的次数严格大于50%50\%,求这个数。

怎么做?

朴素桶计数

我们第一想到的,至少我第一想到的就是桶计数。出现一个数,把它扔进桶中。桶计数是很基本的概念,如果你这都不懂,赶紧百度去!
代码实现也很简单,纯手敲吧,有锅不修。

#include <iostream>
#include <cstdio>

int bucket[10086];

int main() {
    int n;
    std :: cin >> n;
    for(int i = 1; i <= n; i++) {
        int x;
        std :: cin >> x;
        bucket[x]++;
    }
    for(int i = 1; i <= 10086; i++)
        if(bucket[i] > n / 2) {
            cout << i << std :: endl;
            break;
        }
    return 0;
}

但是桶计数的局限性非常大,非常容易爆空间,int的范围就可以让这玩意儿直接gg。

如果你想到了离散化,说明你还是很聪明的,蟹蟹为你点赞。但是离散化并不是最好的方法,照样有很多东西会卡离散化。我们直接pass掉桶计数吧。

好吧我知道有些人又开始叫map了……稍安勿躁,我有更好的方案。

排序

这叫做更好的方案????蟹蟹你真是绝了。
先别急着爆踩我,首先一个数列如果存在主元素,则排序后第n2\dfrac{n}{2}个位置上的数一定是主元素。
我们就有了新方法:

#include <iostream>
#include <cstdio>

const int maxn = 10005;
int a[maxn];

int main() {
    int n;
    for(int i = 1; i <= n; i++)
        std :: cin >> a[i];
    
    sort(a + 1, a + n + 1);
    std :: cout << a[n / 2 + 1];
    return 0;
}

很明显此算法的复杂度是O(nlogn)O(n\log n),但还有没有直接O(n)O(n)的做法呢?
还真有。以前还是个初学者的时候看到主元素问题思路就停止在这里了,前天有机会重新见到了这道题,我又有了新的想法。

互相抵消法

我们把序列中所有不相同的两个数互相抵消,剩下的就是主元素。因为主元素在数列中出现的数量严格大于50%50\%,所以这个做法的正确性不难保证。这个方法非常的微妙。
这里可以用很多种实现方式,我选择用stack食用= =

#include <iostream>
#include <cstdio>

const int maxn = 10000005;
int s[maxn];
int top;

int main() {
    int n;
    scanf("%d",&n);
    while(n--) {
        int x;
        scanf("%d",&x);
        s[top++] = x;
        if(top == 1) continue;
        if(s[top - 1] != s[top - 2]) top -= 2;
    }
    printf("%d\n",s[top - 1]);
    return 0;
}

后记

这个问题真的很有趣,也验证了我思维的成长过程。两年前我只能想到O(nlogn)O(n\log n)的做法,如今我却能已经独立思考出O(n)O(n)的做法。所以我在进步,我也会一直进步的。感触真的是颇深。

posted @   dbxxx  阅读(255)  评论(1编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示