题目链接:http://ybt.ssoier.cn:8088/problem_show.php?pid=1186
1.利用桶的思想
时间复杂度为 O ( max ( n , m ) ) O(\max(n, m)) O(max(n,m)),空间复杂度为 O ( m ) O(m) O(m)
#include <iostream>
using namespace std;
const int N = 1010, M = 110;
int n, bucket[M];
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
int x;
cin >> x;
bucket[x + 50]++;
}
for (int i = 0; i <= 100; i++)
{
if (bucket[i] > n / 2)
{
cout << i - 50 << endl;
return 0;
}
}
cout << "no" << endl;
return 0;
}
2.摩尔投票法
摩尔投票算法(Boyer-Moore majority vote algorithm)的思想是对拼消耗,从第一位选手开始闯关,遇到“非同类”就“同归于尽”,遇到“同类”就相伴而行,最后剩下的选手即为所求。
如果数组中有一个数字出现的次数超过数组长度的一半,那么这个数字出现的次数比其他所有数字出现的次数的和还要多。
初始时 r e s = a [ 0 ] res=a[0] res=a[0], c n t = 0 cnt = 0 cnt=0,遍历数组的时候更新答案 r e s res res 和次数 c n t cnt cnt:
- 如果 a [ i ] a[i] a[i] 和之前保存的数字 r e s res res 相同,则次数 c n t cnt cnt 加 1 1 1;
- 如果 a [ i ] a[i] a[i] 和之前保存的数字 r e s res res 不相同,则次数 c n t cnt cnt 减 1 1 1;
- 如果次数为 0 0 0,则保存 a [ i ] a[i] a[i],并把次数 c n t cnt cnt 设为 1 1 1。
由于要找的数字出现的次数比其他所有数字出现的次数之和还要多,则最后一次把次数设为 1 1 1 时对应的数字就是要找的数字。
注意:在任何数组中,出现次数超过总数一半的数一定最多只有一个。
如果数组中存在这样的数,那么摩尔投票法最后得到的结果即为所求。
如果数组中不存在这样的数,例如 [ 1 , 2 , 1 , 2 , 3 ] [1, 2, 1, 2, 3] [1,2,1,2,3],则要校验最后的 r e s res res 的出现次数是否超过了数组长度的一半,即要重新遍历数组、统计出现的次数。
时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)
#include <iostream>
using namespace std;
const int N = 1010;
int n, a[N];
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
int res = a[0], cnt = 0;
for (int i = 1; i < n; i++)
{
if (cnt == 0)
{
res = a[i];
cnt = 1;
}
else
{
if (a[i] != res)
{
cnt--;
}
else
{
cnt++;
}
}
}
// 最后要校验res的出现次数是否超过了一半
int t = 0;
for (int i = 0; i < n; i++)
{
t += (a[i] == res);
}
if (t > n / 2)
{
cout << res << endl;
}
else
{
cout << "no" << endl;
}
return 0;
}