我们先来看问题描述:春节期间小明使用微信收到很多个红包,非常开心。在查看领取红包记录时发现,某个红包金额出现的次数超过了红包总数的一半。请帮小明找到该红包金额。写出具体的算法思路和代码实现。要求算法尽可能高效。
给定一个红包的金额数组gifts及它的大小,请返回所求红包的金额。
测试样例:
[1,2,3,2,2] 5
输出 2
在给出代码之前先给大家分析一下题意:该题就是在数组中找出出现次数超过一的数字,要求时间复杂度比较低。这里有个问题就是如果已经明确数组中一定存在超过数组长度一半的数,否则我们就要进行判断。
(1)我们认为数组中不一定存在超过数组长度的数:
(a)第一个思路:先对数组进行排序,然后在判断数组中间的那位数出现的次数,当超过一半时,返回该数,否则返回0,代码如下:
//快排
void quick_sort(int *a,int left,int right)
{
int x;
int i = left;
int j = right;
int value = a[left];
if(left < right){
while(i < j){
for(;j > i && a[j] > value;j--);
if(j > i){
a[i++] = a[j];
}
for(;i < j && a[i] < value;i++);
if(i < j){
a[j--] = a[i];
}
}
a[i] = value;
quick_sort(a,left,i - 1);
quick_sort(a,i + 1,right);
}
}
//得到数组中出现次数最多的数
int getthesame_value(int *gifts,int n)
{
int ans = 0;
int i;
int cnt = 0;
if(gifts == NULL || n < 0){
return -1;
}
//排序
quick_sort(gifts,0, n - 1);
ans = gifts[n / 2];
for(i = 0;i < n;i++){
if(gifts[i] == ans){
cnt++;
}
}
return cnt > n / 2 ? ans : 0;
}
其执行结果:
其时间复杂度:
快排+遍历:o(log(2^n) + n)
(2)利用哈希思想,这时就比较消耗内存:
//使用哈希思想
int hash_value(int *gifts,int n)
{
int i ;
int t = 0;
int index;
int *hash_table = (int *)malloc(sizeof(int) * N);
bzero(hash_table,sizeof(int) * N);
if(hash_table == NULL){
return -1;
}
for(i = 0 ;i < n;i++){
hash_table[gifts[i]]++;
}
for(i = 0;i < N;i++){
if(hash_table[i] == 0){
continue;
}else{
if(hash_table[i] > t){
t = hash_table[i];
index = i;
}
}
}
return t > n / 2 ? index : 0;
}
执行结果:
主程序:
int main(int argc,char**argv)
{
int n;
int a[N];
int i;
int value;
while(1){
printf("请输入数组长度n:(-1 quit)\n");
scanf("%d",&n);
if(n == -1){
printf("error!!\n");
exit(1);
}
printf("请输入数组元素:\n");
for(i = 0;i < n;i++){
scanf("%d",a + i);
}
value = getthesame_value(a,n);
//value = hash_value(a,n);
printf("%d\n",value);
}
}
以上就是数组中不一定存在超过数组长度一半的数。
(2)当我们已经明确数组中一定存在超过数组长度一半的数。
(a)第一个思路还是先将数组进行排序,我们已经知道数组中一定存在超过数组一半长度的数,这时就可以直接返回a[n / 2]不用在遍历。。代码和上述的差不多,在这不再赘述。
(b)第二种思路就是,不进行排序,时间复杂度会降低,具体的思路是:如果每次删除数组中两个不同的数,那么在剩下的数中,出现次数超过一半的数还是那个数,并没有发生变化,可以通过不断重复这个过程,把数组的长度降低,从而得到答案。时间复杂度只有o(n).其代码如下:
int find(int *a,int n)
{
int result;
int time,i;
for(i = time = 0;i < n;i++){
if(time == 0){
result = a[i];
time = 1;
}else{
if(result == a[i]){
time++;
}else{
time--;
}
}
}
return result;
}
其执行结果如下:
该段代码只能用于已经明确数组中一定存在一个数超过数组长度的一半!!!!