我們先來看問題描述:春節期間小明使用微信收到很多個紅包,非常開心。在查看領取紅包記錄時發現,某個紅包金額出現的次數超過了紅包總數的一半。請幫小明找到該紅包金額。寫出具體的算法思路和代碼實現。要求算法儘可能高效。
給定一個紅包的金額數組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;
}
其執行結果如下:
該段代碼只能用於已經明確數組中一定存在一個數超過數組長度的一半!!!!