参考博客:
https://blog.csdn.net/mdj67887500/article/details/7038850
https://blog.csdn.net/mdj67887500/article/details/7039624
如有侵权,联系删除。
本博客主要是记录模板,至于讲解的话,请参见原博客。
1、k=2,即求数组中出现次数大于数组长度一半的元素。
① 如果数组a是有序数组,那么一个在线处理就可以搞定;
②更一般的情况,a不确定是否有序,那么就要采用下面的方法了。
两种方法时间复杂度是O(n)的。
思路:
因为必定存在一个数出现次数大于n/2,每次删除两个不同的元素,最后剩下的就是要求的元素。
证明:
设目标数为
t
a
r
g
e
t
target
target,出现次数为
t
i
m
e
>
n
2
time>\frac{n}{2}
time>2n ;
①当删除两个不同的元素,若其中一个是
t
a
r
g
e
t
target
target,则
t
i
m
e
−
−
,
n
−
=
2
time--,n-=2
time−−,n−=2
因为
t
i
m
e
>
n
2
time>\frac{n}{2}
time>2n
所以
t
i
m
e
−
1
>
n
2
−
1
=
(
n
−
2
)
2
time-1>\frac{n}{2}-1=\frac{(n-2)}{2}
time−1>2n−1=2(n−2)
②当删除两个不同的元素,两个元素都不是
t
a
r
g
e
t
target
target,只需要改
n
−
=
2
n-=2
n−=2。
t
i
m
e
>
n
−
2
2
time>\frac{n-2}{2}
time>2n−2 。
代码:
int find_two(int a[],int n)
{
int tmp=a[0],cnt=0;
for(int i=1; i<=n; i++)
{
if(cnt==0)
{
tmp=a[i];
cnt++;
}
else if(tmp==a[i])
cnt++;
else
cnt-;
}
return tmp;
}
2、k>2
note:出现次数大于
n
k
\frac{n}{k}
kn的数至多有
k
−
1
k-1
k−1个,
e
g
:
n
3
eg:\frac{n}{3}
eg:3n 至多有2个
复杂度:O(nlog(k))
#include <bits/stdc++.h>
#include <cstdio>
//#define local
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int N = 5e6+5;
const int inf = 0x3f3f3f3f;
int n;
int a[N];
struct node
{
int v;
mutable int c;
};
struct cmp
{
bool operator()(const node& a, const node& b)
{
return a.v<b.v;
}
};
int find_k(int a[],int n,int k)
{
int siz=0;//记录t中不同的元素个数,t的大小
set<node,cmp> t;
for(int i=1;i<=n;i++)
{
node p;
p.v=a[i];
p.c=1;
set<node,cmp>::iterator pos=t.find(p);
if(pos==t.end())//没有找到b[i]
{
t.insert(p);
siz++;
if(siz==k)//t的大小,不同元素数量==k
{
for(set<node,cmp>::iterator it=t.begin();it!=t.end();)
{
if(it->c >1)
{
(it->c)--;
it++;
}
else t.erase(it++);
}
siz=t.size();//更新siz的值
}
}
else
{
(pos->c)++;
}
}
for(set<node>::iterator it=t.begin();it!=t.end();it++)//检验
{
if(it->c >= n/k)
return it->v;
}
}
int main()
{
#ifdef local
freopen("input.txt","r",stdin);
#endif // local
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
printf("%d",find_k(a,n,2));
return 0;
}