1. 题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
假设数组非空,如果不存在满足条件的数字,返回-1
。
思考题:
假设要求只能使用 O ( n ) O(n) O(n) 的时间和额外 O ( 1 ) O(1) O(1) 的空间,该怎么做呢?
样例1
输入:
5
1 2 1 1 3
输出:
1
样例2
输入:
5
1 1 2 2 3
输出:
-1
2. 摩尔投票法
摩尔投票算法的时间和空间都很低,时间复杂度为O(n),空间复杂度为O(1)。
算法原理:
每次从数组中找出一对不同的元素,将它们从数组中删除,直到遍历完整个数组。由于这道题已经说明一定存在一个出现次数超过一半的元素,所以遍历完数组后数组中一定会存在至少一个元素。
算法步骤:
- 定义全局技术器变量 c n t cnt cnt(初值为 0 0 0),当前答案变量 v a l val val。开始遍历数组,当前元素为 x x x。
- 若 c n t = = 0 cnt == 0 cnt==0,则 v a l = x , c n t + + val = x,cnt++ val=x,cnt++;(假设答案是基佬,有唯一的一个房间,基佬看到房间没人,就进去,基佬数+1)。
- 若 c n t ! = 0 cnt != 0 cnt!=0,且 v a l = = x val == x val==x,则 c n t + + cnt++ cnt++;(房间里有基佬,来了新的基佬,基佬喜欢和基佬在一起,就进了房间,基佬数+1)。
- 若 c n t ! = 0 cnt != 0 cnt!=0,且 v a l ! = x val != x val!=x,则 c n t − − cnt-- cnt−−;(房间里有基佬,这时候来了一个妹子,妹子从房间里带走一个基佬,基佬数-1)。
- 数组每个元素按照 1 4 1~4 1 4 操作走一遍,最后的 v a l val val 就代表算法求得的答案值(但数组不一定有大于一半的元素存在)。
无解情况:
如果数组中不存在大于数组长度一半的元素,如样例2中所示,则拿着算法求得的
v
a
l
val
val 去一个个对比数组元素,如果超过了
n
/
2
n/2
n/2 的数量,则说明有解,解就是
v
a
l
val
val;否则说明无解。
3. 代码
#include <iostream>
using namespace std;
const int N = 2010;
int n;
int a[N];
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
int cnt = 0, val;
for (int i = 0; i < n; i++)
{
if (!cnt) val = a[i], cnt++;
else if (val == x) cnt++;
else cnt--;
}
cnt = 0;
for (int i = 0; i < n; i++) if (a[i] == val) cnt++;
if (cnt > n / 2) printf("%d\n", val);
else puts("-1");
return 0;
}