劍指 offer程式碼解析——面試題38數字在排序陣列中出現的次數
阿新 • • 發佈:2022-05-03
題目:統計一個有序陣列中K出現的次數。
分析:本題最直觀的思路就是遍歷陣列,統計K出現的次數即可。
這種方式的時間複雜度為O(n)。下面我們充分利用“有序陣列”這一條件,提高演算法的時間效率。
對於一個有序陣列,所有的數字K一定都集中在一起,因此只要我們找到這一組K的頭和尾就能知道K出現的次數。
此時問題就轉化為:在一個有序陣列中尋找某個數字。
我們很自然地就想到了二分搜尋,在目前所有的搜尋演算法中,二分搜尋具有最高的搜尋效率。
對於本題,我們需要進行兩次二分搜尋,一次尋找K的頭,一次尋找K的尾。
二分搜尋K頭部的過程如下:
1.確定當前陣列的中點;
2.若中點<K,則K的起點在後半段;
3.若中點>K,則K的起點在前半段;
4.若中點=K,則判斷中點的前一個點是否等於K;
4.1.若等於K,則K的起點在前半段;
4.2.若不等於K,則該中點即為K的起點。
尋找K的終點與尋找起點類似。本演算法的具體程式碼如下:
/** * 題目:統計一個有序陣列中K出現的次數。 * @author 大閒人柴毛毛 * @date 2016年3月25日 */ public class CountKNumber { /** * 分析:本題最直觀的思路就是遍歷陣列,統計K出現的次數即可。 * 這種方式的時間複雜度為O(n)。下面我們充分利用“有序陣列”這一條件,提高演算法的時間效率。 * * 對於一個有序陣列,所有的數字K一定都集中在一起,因此只要我們找到這一組K的頭和尾就能知道K出現的次數。 * 此時問題就轉化為:在一個有序陣列中尋找某個數字。 * 我們很自然地就想到了二分搜尋,在目前所有的搜尋演算法中,二分搜尋具有最高的搜尋效率。 * 對於本題,我們需要進行兩次二分搜尋,一次尋找K的頭,一次尋找K的尾。 * 二分搜尋K頭部的過程如下: * 1.確定當前陣列的中點; * 2.若中點<K,則K的起點在後半段; * 3.若中點>K,則K的起點在前半段; * 4.若中點=K,則判斷中點的前一個點是否等於K; * 4.1.若等於K,則K的起點在前半段; * 4.2.若不等於K,則該中點即為K的起點。 * 尋找K的終點與尋找起點類似。 * * 本演算法的具體程式碼如下: */ /** * 獲取陣列中K出現的個數 * @param a 陣列 * @param k * @return 返回K出現的個數(若為-1表示獲取失敗) */ public static int getKNumber(int[] a,int k){ //健壯性判斷:若陣列為空 if(a==null || a.length<=0){ System.out.println("陣列為空!"); return -1; } //子陣列起點的下標 int start = 0; //子陣列終點的下標 int end = a.length-1; //K起點的下標 int k_start = -1; //K終點的下標 int k_end = -1; //當子陣列的長度大於0的時候一直迴圈,獲取k的起點座標 while(end-start >= 0){ //計算中點下標 int mid = (start+end)/2; //若a[mid]>k,則k的起點在前半段 if(a[mid]>k){ end = mid-1; } //若a[mid]<k,則k的起點在後半段 else if(a[mid]<k){ start = mid+1; } //若a[mid]=k else{ //若a[mid-1]==k,則說明a[mid]不是k的起點 if(a[mid-1]==k){ end = mid-1; } //若a[mid-1]!=k,則說明a[mid]是k的起點 else{ k_start = mid; break; } } } //將start、end指向陣列的頭和尾 start = 0; end = a.length-1; //當子陣列的長度大於0的時候一直迴圈,獲取k的終點座標 while(end-start >= 0){ //計算中點下標 int mid = (start+end)/2; //若a[mid]>k,則k的起點在後半段 if(a[mid]>k){ end = mid-1; } //若a[mid]<k,則k的起點在前半段 else if(a[mid]<k){ start = mid+1; } //若a[mid]=k else{ //若a[mid+1]==k,則說明a[mid]不是k的終點 if(a[mid+1]==k){ start = mid+1; } //若a[mid+1]!=k,則說明a[mid]是k的終點 else{ k_end = mid; break; } } } //若未找到k的起點或終點 if(k_start==-1 || k_end==-1) return 0; //統計k的個數 return k_end-k_start+1; } /** * 測試 */ public static void main(String[] args){ //構建陣列 int[] a = {0,1,2,3,4,6,7,7,7,7,7,7,8,9}; //統計k的個數 System.out.println(getKNumber(a,7)); } }