題目:數組中有一個數字出現超過數組長度的一半,請找出這個數字。列入輸入一個長度爲9的數組{1,2,3,2,2,2,5,4,2},由於數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。
看到題目的第一感覺就是要先把未排序的數組排序,排序的最小時間複雜度(快速排序) O(N*logN) , 加上遍歷,時間複雜度爲: O(N*logN+N)
第二種方法是考慮每次刪除兩個不同的數,那麼在剩下的數中,出現的次數仍然超過總數的一半。通過不斷重複這個過程,不斷排除掉其它的數,最終找到那個出現次數超過一半的數字。這個方法,免去了上述思路一、二的排序,也避免了思路三空間O(N)的開銷,總得說來,時間複雜度只有O(N),空間複雜度爲O(1),不失爲最佳方法。
還有就是可以利用哈希表。如果所選的數組的元素比較集中的話可以使用哈希表統計每個元素出現的次數,時間複雜度是O(N),但是題目並沒有給出數組元素的範圍。次方法也不太好。
有了上面的思路後,我們不難寫出這樣實現代碼:
方法一:使用快排的思想實現
int Partition(int A[],int low,int high) { int pivot=A[low]; while(low <high) { while(low<high && A[high]>=pivot) --high; A[low]=A[high]; while(low<high && A[low]<=pivot) ++low; A[high]=A[low]; } A[low]=pivot; return low; } int HalfData(int a[],int len) { int start=0; int end=len-1; int middle=len >> 1; int index=Partition(a,start,end); printf("index1:%d\n",index); while(index != middle) { if(index > middle) { end=index-1; index=Partition(a,start,end); } else { start=index+1; index=Partition(a,start,end); } } return a[index]; }
方法二:遍歷數組時保存兩個值,一個是數組中的數字,另一個是次數,當遍歷到下一個數字時,如果下一個數字與之前保存的數字相同,則次數加1,如果不同,則次數減1,如果次數爲0,則需要保存下一個數字,並把次數設定爲1。由於我們要找的數字出現的次數比其他所有數字的出現次數之和還要大,則要找的數字肯定是組後一次把次數設爲1時對應的數字。該方法的時間複雜度爲O(n),空間複雜度爲O(1)。
int MoreThanHalfNum(int* numbers, unsigned int length) { if(numbers == NULL && length == 0) { g_bInputInvalid = true; return 0; } g_bInputInvalid = false; int result = numbers[0]; int times = 1; for(int i = 1; i < length; ++i) { if(times == 0) { result = numbers[i]; times = 1; } else if(numbers[i] == result) times++; else times--; } times = 0; for(int i = 0; i < length; ++i) { if(numbers[i] == result) times++; } if(times * 2 <= length) { g_bInputInvalid = true; result = 0; } return result; }