正文
本文參考自《劍指offer》一書,代碼采用Java語言。
題目
統計一個數字在排序數組中出現的次數。例如輸入排序數組{1, 2, 3, 3,3, 3, 4, 5}和數字3,由於3在這個數組中出現了4次,因此輸出4。
思路
分析:對於例子來說,如果采用二分法找到某一個3后,再往前遍歷和往后遍歷到第一個和最后一個3,在長度為n的數組中有可能出現O(n)個3,因此這樣的掃描方法時間復雜度為O(n),效率與從頭到尾掃描一樣,速度太慢。
這題關鍵是找到第一個和最后一個3,因此我們嘗試改進二分法:中間數字比3大或者小的情況與之前類似,關鍵是中間數字等於3的情況,這時可以分類討論如下:
1)如果中間數字的前一個數字也等於3,說明第一個3在前面,繼續在前半段查找第一個3;
2)如果中間數字的前一個數字不等於3,說明該位置是第一個3;
3)如果中間數字的后一個數字也等於3,說明最后一個3在后面,繼續在后半段查找最后一個3;
2)如果中間數字的后一個數字不等於3,說明該位置是最后一個3;
附加:牛客網上還有一種算法:如果找數字k的次數,由於數組是整數,可以直接找k-0.5和k+0.5應該在數組中哪個位置,這種方法就不用討論這么多情況了。(不過double類型的大小比較不知道是否會增加太多時間消耗)。
測試算例
1.功能測試(數字出現次數為0、1、2等)
2.邊界值測試(數組只有一個數字,查找數字為第一個或者最后一個)
2.特殊測試(null)
Java代碼
//題目:統計一個數字在排序數組中出現的次數。例如輸入排序數組{1, 2, 3, 3,
//3, 3, 4, 5}和數字3,由於3在這個數組中出現了4次,因此輸出4。
public class NumberOfK {
public int GetNumberOfK(int [] array , int k) {
if(array==null || array.length<=0)
return 0;
int firstK = getFirstK(array,0,array.length-1,k);
if(firstK == -1)
return 0;
int lastK = getLastK(array,firstK,array.length-1,k);
return lastK-firstK+1;
}
private int getFirstK(int[] arr, int start, int end,int k){
if(start>end)
return -1;
int mid = (start+end)>>1;
if(arr[mid]==k){
if( mid == 0 ||arr[mid-1]!=k )
return mid;
else
end = mid-1;
}else if(arr[mid]<k){
start = mid+1;
}else{
end = mid-1;
}
return getFirstK(arr,start,end,k);
}
private int getLastK(int[] arr, int start, int end,int k){
if(start>end)
return -1;
int mid = (start+end)>>1;
if(arr[mid]==k){
if(mid==arr.length-1 || arr[mid+1]!=k )
return mid;
else
start = mid+1;
}else if(arr[mid]<k){
start = mid+1;
}else{
end = mid-1;
}
return getLastK(arr,start,end,k);
}
}
解法二:尋找k+0.5和k-0.5的方法:
public int GetNumberOfK(int [] arr , int k) {
if(arr==null || arr.length<=0)
return 0;
int first = getLoc(arr, k , k-0.5);
int last = getLoc(arr,k,k+0.5);
return last-first;
}
private int getLoc(int[]arr, int k, double m){ //同樣是二分查找
int start=0,end=arr.length-1;
while(start<=end){
int mid=(start+end)>>1;
if(arr[mid]>m){
end=mid-1;
}else{
start=mid+1;
}
}
return start;
}
收獲
1.同53-3
