cvmachine.com - 申博开户网

查找: 您的方位主页 > 手机频道 > 阅览资讯:【剑指offer】呈现次数超越一半的数字

【剑指offer】呈现次数超越一半的数字

2019-04-03 06:22:31 来历:www.cvmachine.com 【

转载请注明出处:http://blog.csdn.net/ns_code/article/details/26957383

 

标题描绘:

数组中有一个数字呈现的次数超越数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。因为数字2在数组中呈现了5次,超越数组长度的一半,因而输出2。

输入:

每个测验事例包含2行:

榜首行输入一个整数n(1<=n<=100000),表明数组中元素的个数。

第二行输入n个整数,表明数组中的每个元素,这n个整数的规模是[1,1000000000]。

输出:

对应每个测验事例,输出呈现的次数超越数组长度的一半的数,假如没有输出-1。

样例输入:
9
1 2 3 2 2 2 5 4 2
样例输出:
2
思路:

1、一个数字在数组中呈现次数超越了一半,则排序后,坐落数组中心的数字必定就是该呈现次数超越了长度一半的数字(能够用反证法证明),也就是说,这个数字就是计算学上的中位数。最简单想到的办法是用快速排序对数组排序号后,直接取出中心的那个数字,这样的时刻复杂度为O(nlogn),空间复杂度为O(1)。

2、事实上能够不必对数组进行排序,或者说仅部分排序,受快速排序的partition函数的启示,咱们能够运用重复调用partition函数来求的该数字。咱们现在数组中随机选取一个数字,然后经过Partition函数回来该数字在数组中的索引index,假如index刚好等于n/2,则这个数字就是数组的中位数,也就是要求的数,假如index大于n/2,则中位数肯定在index的左面,在左面持续寻觅即可,反之在右边寻觅。这样能够只在index的一边寻觅,而不必两头都排序,减少了一半排序时刻。这种状况的均匀时刻复杂度大致为:T(n) = n+n/2+n/4+n/8+....+1,很明显当n很大时,T(n)趋近于2n,也就是说均匀状况下时刻复杂度为O(n),可是这种状况下,最坏的时刻复杂度仍然为O(n*n),最坏状况下,index总是坐落数组的最左或最右边,这样时刻复杂度为T(n) = n+n-1+n-2+n-3+....+1 = n(n-1)/2,明显,时刻复杂度为O(n*n),空间复杂度为O(1)。

我用该办法写了下代码,并在九度OJ上run,报了超时,代码如下:

 

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<stdbool.h>
bool IsExisted;

void Swap(int *a,int *b)
{
if(*a != *b)
{
*a = *a + *b;
*b = *a - *b;
*a = *a - *b;
}

}

/*
算法导论版快排的Partition函数
*/
int Partition(int *A,int low,int high)
{
if(A==NULL || low<0 || high<0 || low>=high)
return -1;

int small = low-1;
int j;
for(j=low;j<high;j++)
{
if(A[j] <= A[high])
{
++small;
if(j != small)
Swap(&A[j],&A[small]);
}
}
++small;
Swap(&A[small],&A[high]);
return small;
}

int Random_Partition(int *A,int low,int high)
{
//设置随机种子
srand((unsigned)time(0));
int index = low + rand()%(high-low+1);
Swap(&A[index],&A[high]);
return Partition(A,low,high);
}


/*
判别关键字key在数组A中呈现的次数是否超越一半
*/
bool IsMoreThanHalf(int *A,int len,int key)
{
int times = 0;
int i;
for(i=0;i<len;i++)
if(A[i] == key)
times++;
if(2*times <= len)
return false;
else
return true;
}
 
/*
回来数组A中呈现次数超越一半的数字
根据Partition函数的完结
*/
int MoreThanHalf(int *A,int len)
{
if(A==NULL || len<1)
{
IsExisted = false;
return -1;
}

int low = 0;
int high = len-1;
int mid = len>>1;
int index = Random_Partition(A,low,high);
while(index != mid)
{
if(index > mid)
index = Random_Partition(A,low,index-1);
else
index = Random_Partition(A,index+1,high);
}

int key = A[mid];
if(!IsMoreThanHalf(A,len,key))
{
IsExisted = false;
return -1;
}
return key;
}

int main()
{
int n;
while(scanf("%d",&n) != EOF)
{
int *A = (int *)malloc(sizeof(int)*n);
if(A == NULL)
exit(EXIT_FAILURE);

int i;
for(i=0;i<n;i++)
scanf("%d",A+i);

IsExisted = true;
int key = MoreThanHalf(A,n);
if(IsExisted)
printf("%d\n",key);
else
printf("-1\n");
}
return 0;
}
明显,这并不算太好的办法,并且每次随机选取枢轴元素后,都要进行交流操作,且在每次的移动过程中,也要进行交流操作,比较耗时。

算法导论第九章上给出了一种根据Partition的在最坏状况下也能以O(n)运转的挑选第k小的数字的办法(挑选中位数是挑选地k小元素的特殊状况,这儿k=n/2),这种办法能够确保挑选的枢轴元素在中位数的邻近,算法导论上并给出了具体的证明,证明该办法在最坏状况下的时刻复杂度也为O(n)。

在修正区分算法后,咱们经过以下过程来完结在n个元素的数组中找第i小元素的SELECT:
1、把数组A分红ceiling(n/5)个组,除最终一组外,每组都有5个元素,最终一组有n mod 5个元素;
2、对每组(的五个元素)用插入法进行排序,然后选出该组的中位数,即排好序的第三个元素;
3、关于过程2中得到的一切的中位数,经过递归调用SELECT来找到它们的(下)中位数x,(也就是找到第2步得到的一切中位数中第floor(ceiling(n/5) / 2)小个元素);
4、运用修正后的区分算法把元素分红小于x和大于x的两个子数组。假如设k为区分低区的元素个数加一,则x就是A中第k小的元素;
5、假如i = k,那咱们就回来x,它就是咱们要找的值。假如i < k,咱们就在第4步里的区分低区持续递归调用SELECT来找到第i小的元素;假如i > k,咱们就在区分高区递归调用SELECT找第i-k小的数。

需求留意的是,算法中每个分组巨细为5,假如改成3是不可的(每组为3的时刻复杂度为O(NlgN)。假如分红组数为奇数的话,每组巨细要>=5才干确保O(N)的时刻。

3、考虑用哈希,key保存数组元素,value保存呈现的次数,这样在遍历O(n)能做出key-value的映射,再用O(k)(k为需求的槽的个数)能够找出呈现次数超越一半的key,可是因为数组中元素的巨细规模不知道,因而运用这种办法,首要不能确认哈希表的巨细,即便经过遍历一次求得了最大值,规模很大的话,又要花费很大心思规划很好的哈希函数来完结key-value的映射,且不具有通用性,并且还要考虑数组中元素为负值的状况,因而用哈希表不合适。

4、网上很盛行的做法,因为该数字的呈现次数比一切其他数字呈现次数的和还要多,因而能够考虑在遍历数组时保存两个值:一个是数组中的一个数字,一个是次数,。当遍历到下一个数字时,假如下一个数字与之前保存的数字相同,则次数加1,假如不同,则次数减1,假如次数为0,则需求保存下一个数字,并把次数设定为1。因为咱们要找的数字呈现的次数比其他一切数字的呈现次数之和还要大,则要找的数字肯定是组后一次把次数设为1时对应的数字。该办法的时刻复杂度为O(n),空间复杂度为O(1)。

用该思路写出的代码AC,如下:

 

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
bool IsExisted;

/*
判别关键字key在数组A中呈现的次数是否超越一半
*/
bool IsMoreThanHalf(int *A,int len,int key)
{
int times = 0;
int i;
for(i=0;i<len;i++)
if(A[i] == key)
times++;
if(2*times <= len)
return false;
else
return true;
}

/*
找出呈现次数超越一半的数字
*/
int MoreThanHalfDP(int *A,int len)
{
if(A==NULL || len<1)
{
IsExisted = false;
return -1;
}

int result = A[0];
int times = 1;
int i;
for(i=1;i<len;++i)
{
if(times == 0)
{
result = A[i];
times = 1;
}
else if(A[i] == result)
++times;
else
--times;
}
if(!IsMoreThanHalf(A,len,result))
{
IsExisted = false;
return -1;
}
return result;
}

int main()
{
int n;
while(scanf("%d",&n) != EOF)
{
int *A = (int *)malloc(sizeof(int)*n);
if(A == NULL)
exit(EXIT_FAILURE);

int i;
for(i=0;i<n;i++)
scanf("%d",A+i);

IsExisted = true;
int key = MoreThanHalfDP(A,n);
if(IsExisted)
printf("%d\n",key);
else
printf("-1\n");
}
return 0;
}
/**************************************************************
Problem: 1370
User: mmc_maodun
Language: C
Result: Accepted
Time:50 ms
Memory:1304 kb
****************************************************************/

 
 

本文地址:http://www.cvmachine.com/a/luyou/100224.html
m88 188bet uedbet 威廉希尔 明升 bwin 明升88 bodog bwin 明升m88.com 18luck 188bet unibet unibet Ladbrokes Ladbrokes casino m88明升 明升 明升 m88.com 188bet m88 明陞 uedbet赫塔菲官网 365bet官网 m88 help
188bet www.188bet.com bwin 平博 unibet 明升 188bet uk Ladbrokes 德赢vwin 188bet m88.com w88 平博88 uedbet体育 188bet 188bet 威廉希尔 明升体育app 平博88 M88 Games vwin德赢 uedbet官网 bodog fun88 188bet
Tags: 呈现 剑指 offer
修改:申博开户网
关于咱们 | 联络咱们 | 友情链接 | 网站地图 | Sitemap | App | 回来顶部