本文參考自《劍指offer》一書,代碼采用Java語言。
題目
數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度為9的數組{1, 2, 3, 2, 2, 2, 5, 4, 2}。由於數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。
思路
思路一:數字次數超過一半,則說明:排序之后數組中間的數字一定就是所求的數字。
利用partition()函數獲得某一隨機數字,其余數字按大小排在該數字的左右。若該數字下標剛好為n/2,則該數字即為所求數字;若小於n/2,則在右邊部分繼續查找;反之,左邊部分查找。
思路二:數字次數超過一半,則說明:該數字出現的次數比其他數字之和還多
遍歷數組過程中保存兩個值:一個是數組中某一數字,另一個是次數。遍歷到下一個數字時,若與保存數字相同,則次數加1,反之減1。若次數=0,則保存下一個數字,次數重新設置為1。由於要找的數字出現的次數比其他數字之和還多,那么要找的數字肯定是最后一次把次數設置為1的數字。
采用陣地攻守的思想:
第一個數字作為第一個士兵,守陣地;count = 1;
遇到相同元素,count++;
遇到不相同元素,即為敵人,同歸於盡,count--;當遇到count為0的情況,又以新的i值作為守陣地的士兵,繼續下去,到最后還留在陣地上的士兵,有可能是主元素。
再加一次循環,記錄這個士兵的個數看是否大於數組一般即可。
測試算例
1.功能測試(存在或者不存在超過數組長度一半的數字)
2.特殊測試(null、1個數字)
Java代碼
//題目:數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例
//如輸入一個長度為9的數組{1, 2, 3, 2, 2, 2, 5, 4, 2}。由於數字2在數組中
//出現了5次,超過數組長度的一半,因此輸出2。
public class MoreThanHalfNumber {
boolean isInputInvalid = true;
//方法一:partition方法
public int MoreThanHalfNum_Solution(int [] array) {
if(array==null ||array.length<=0)
return 0;
int low=0;
int high=array.length-1;
int index=partition(array,low,high);
while(index!=array.length>>1){
if(index<array.length>>1){
low=index+1;
index=partition(array,low,high);
}else{
high=index-1;
index=partition(array,low,high);
}
}
//判斷次數是否超過一半
int num=array[index];
int times=0;
for(int i=0;i<array.length;i++){
if(array[i]==num){
times++;
}
}
if(times*2>array.length){
isInputInvalid=false;
return num;
}
return 0;
}
private int partition(int[] array,int low ,int high){
int pivotKey=array[low];
while(low<high){
while(low<high && array[high]>=pivotKey)
high--;
int temp=array[low];
array[low]=array[high];
array[high]=temp;
while(low<high && array[low]<=pivotKey)
low++;
temp=array[low];
array[low]=array[high];
array[high]=temp;
}
return low;
}
//方法二
public int MoreThanHalfNum_Solution2(int [] array) {
if(array==null || array.length<=0)
return 0;
int num=array[0];
int count=1;
for(int i=1;i<array.length;i++){
if(count==0) {
num=array[i];
count++;
}
else if(array[i]==num)
count++;
else
count--;
}
if(count>0){
int times=0;
for(int i=0;i<array.length;i++){
if(array[i]==num){
times++;
}
}
if(times*2>array.length){
isInputInvalid=false;
return num;
}
}
return 0;
}
}
收獲
1.length/2 用 length>>1 來代替,具有更高的效率;
2.本題中,找到了所求數字,別忘記判斷該數字的次數是否超過一半。
3.題目所要求的返回值為int,所以如果數組不滿足要求時,無法通過返回值來告知是否出錯,所以這道題設置了一個全局變量來進行判斷。調用該方法時,需要記得對全局變量進行檢查。
4.方法一中,采用了partition()函數,該函數會改變修改的數組,因此在面試的時候,需要和面試官討論是否可以修改數組。
5.兩種方法的時間復雜度均為O(n)。
