在数组中找到出现次数大于一半的数
题目描述
给定一个整型数组arr,请打印其中出现次数大于一半的数,如果没有这样的数,请输出-1。
输入描述:
输入包含两行,第一行包含一个整数 n ( 1 ≤ n ≤ 1 0 5 ) n(1 \leq n \leq 10^5) n(1≤n≤105)代表数组长度,第二行包含n个数,代表数组 a r r ( 1 ≤ a r r i ≤ 1 0 9 ) arr(1 \leq arr_i \leq 10^9) arr(1≤arri≤109)。
输出描述:
输出一个整数,代表出现次数大于一半的数,如果没有这样的数,请输出‘-1“。
示例1
输入
5
11 7 5 7 7
输出
7
示例2
输入
4
2 2 3 3
输出
-1
备注:
时间复杂度 O ( n ) O(n) O(n),额外空间复杂度 O ( 1 ) O(1) O(1)。
题解:
常规解法可以尝试使用哈希表记录每个元素出现的次数,但是额外空间复杂度不满足题目要求。考虑一个比较新颖的做法:每次我们删除两个不一样的元素,如果序列中存在出现次数超过一半的元素,那么该元素最后一定会被保留下来。这样的话,我们可以只用两个变量就搞定了(具体见代码)。
注意:最后保存的不一定是出现次数超过一半的元素,还需要在遍历一边数组,确认一下。
代码:
#include <cstdio>
using namespace std;
const int N = 100010;
int n;
int a[N];
int main(void) {
scanf("%d", &n);
int cand = 0;
int times = 0;
for ( int i = 0; i < n; ++i ) {
scanf("%d", a + i);
if ( !times ) {
cand = a[i];
times = 1;
} else if ( a[i] == cand ) ++times;
else --times;
}
if ( !cand ) return 0 * puts("-1");
times = 0;
int mid = n >> 1;
for ( int i = 0; i < n && times <= mid; ++i ) times += ( a[i] == cand );
if ( times > mid ) printf("%d\n", cand);
else puts("-1");
return 0;
}