一个容易忽视的算法

        今天在做题的时候遇到一个比较有意思的题目,在这里分享给大家,同时介绍一下这个算法。如果大家是在网上买课学习算法知识的话很容易忽视掉这个点(虽然这个知识点考的也不是很频繁)。虽然CSDN上也有这个算法的讲解,但我看了一下都不是特别详细,所以还是打算详细地介绍一下这个算法。

题目

        给定一个大小为N的整数数组,这个数组满足条件A:数组中一定存在一个元素,它的出现次数大于其他所有元素的出现次数之和(注意是严格大于)。请你找出这个数。

        输入格式:共两行,第一行,一个整数n,表示整数数组的大小。接下来一行有n个整数。输出格式:共一行,输出找到符合题目要求的那个数。

        数据范围:1\leqslant N\leqslant 10^{7},对于数组的每一个元素A_{i},都有:1\leq A_{i}\leq 10^{9}。要求:时间限制:3 seconds。空间限制:4 megabytes

常见思路

        接下来,我们先介绍几种常见的解题思路:

暴力搜索

        首先我们自然而然地可以想到用暴力搜索的方法。我们统计每个元素出现的次数,然后输出出现次数最多的那个元素。

#include <iostream>
#include <unordered_map>
#include <vector>

std::unordered_map<int, int> countElements(const std::vector<int>& arr) {
    std::unordered_map<int, int> counts;
    for (int element : arr) {
        if (counts.find(element) != counts.end()) {
            counts[element]++;
        } else {
            counts[element] = 1;
        }
    }
    return counts;
}

int main() {
    std::vector<int> array;
    std::unordered_map<int, int> elementCounts = countElements(array);
    int n;
    scanf("%d",&n);
    while(n--){
        int x;
        scanf("%d",&x);
        array.push_back(x);
    }
    for (const auto& pair : elementCounts) {
        if(pair.second>n/2){
            printf("%d",pair.first);
            break;
        }
    }

    return 0;
}

快排 

        我们注意到,对于一个已经排好序的数组,如果一个元素出现的次数超过数组大小的一半,那么这个数组中间的元素一定就是我们要找的元素,因此可以有如下代码:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int>arr;
int main()
{
	int n;
	scanf("%d",&n);
	int p=n;
	while(p--){
		int x;
		scanf("%d",&x);
		arr.push_back(x);
	}
	sort(arr,arr+n);
	printf("%d",arr[n/2]);
	return 0;
}

        然而,上述两种方法我们都要存入整个数组。而10的7次方个int的大小大约在40MB左右,远远大于题目要求,因此我们需要一种别的方法。 

摩尔投票算法 

        接下来我们要介绍本篇文章的重点:摩尔投票算法。

        下面我们先说明摩尔投票算法的步骤:

  1. 定义一个计数器counter和标记X,两者均初始化为0 。
  2. 当读入一个数y时,如果counter==0 。那么X=y
  3. 如果X==y ,那么counter++
  4. 如果X!=y ,那么counter--

        接下来我们来说明一下为什么这个算法可行。

        首先,很显然,当计数器counter>0 时,表明标记X在当前已读到的所有数中出现频率大于一半,反之亦然。

        接下来最关键的是第二步的操作。注意到:在当counter==0时表示众数正好占所有数的一半,因此,如果读入的数y和X一样,那么赋不赋值都无所谓;如果不一样,读入y后X的频率就会小于一半,因此X一定不是我们要找的数。

        那y有没有可能是我们要找的数呢。我们注意到,此时X已经有n/2-1个,还剩下n/2+1个,如果这歌数组满足条件A,那么剩下的所有元素就必须是y。

        因此我们得出结论,按照摩尔投票算法的步骤,如果这个数组满足条件A,那么标记X一定就是我们想要找的数。

        当然,我们是一个一个数的读入,因此数组不可能在一直满足条件A。但这无所谓,因为我们只需要最后整个数组满足条件A即可,中间步骤无论X变成什么都无所谓。

代码

        最后我们附上这道题的代码。

#include<iostream>
int main(){
	int counter=0,X=0;
	int n;
	scanf("%d",&n);
	while(n--){
		int y;
		scanf("%d",y);
		if(counter==0) X=y;
		if(X==y)counter++;
		else counter--;
	}
	printf("%d",X);
	return 0;
}

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值