1. 程式人生 > >劍指Offer - 數字在排序陣列中出現的次數(Java實現)

劍指Offer - 數字在排序陣列中出現的次數(Java實現)

題目描述:

統計一個數字在排序陣列中出現的次數

思路分析:

已下程式碼均通過牛客測試,主要工作只是在時間複雜度上的優化。
看到排序陣列,然後又看到查詢。首先就想到二分查詢,開始的思路是先通過二分查詢找出需要統計的那個數字,找到後再從那個位置依次向後遍歷。程式碼如下:

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
       
        if(array == null || array.length < 1){
            return 0;
        }
        int left = 0;
        int right = array.length - 1;
        while(right >= left){
            int mid = (left+right)/2;
            if(array[mid]<k){
                left = mid+1;
            }else if(array[mid] > k){
                right = mid-1;
            }else{
                return findNumOfK(array,mid,k);//找到該數字,並向前後依次遍歷,統計出現的次數。
            }
        }
        return 0;
    }
    public int findNumOfK(int[] array, int index, int k){
        int num = 0;
        int lindex = index-1;
        while(lindex >= 0 && array[lindex] == k){
            num++;
            lindex--;
        }
        while(index < array.length && array[index] == k){
            num++;
            index++;
        }
        return num;
    }
}

但是假如陣列中的值全部相同的話,這時候開始的二分查詢就沒有任何意義了,時間複雜度和直接遍歷陣列一樣,為O(N)。所以找到該陣列後還得繼續二分查詢,採用遞迴來實現。
改進後的演算法

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
       
        if(array == null || array.length < 1){
            return 0;
        }
        int left = 0;
        int right = array.length - 1;
        while(right >= left){
            int mid = (left+right)/2;
            if(array[mid]<k){
                left = mid+1;
            }else if(array[mid] > k){
                right = mid-1;
            }else{
                int leftnum =  findNumOfK(array,left,mid,k);//統計右邊出現的次數
                int rightnum = findNumOfK(array,mid+1,right,k);//統計左邊出現的次數。
                return leftnum+rightnum;
            }
        }
        return 0;
    }
    public int findNumOfK(int[] array, int start, int end , int k){
        if(start == end && array[end] ==k){
            return 1;
        }
        while(end >= start){
            int mid = (start+end)/2;
            if(array[mid]<k){
                start= mid+1;
            }else if(array[mid] > k){
                end = mid-1;
            }else{
                int leftnum =  findNumOfK(array,start,mid,k);統計左邊出現的次數。
                int rightnum = findNumOfK(array,mid+1,end,k);統計右邊出現的次數
                return leftnum+rightnum;
            }
        }
        return 0;
    }
}

但是又想到一個問題,該遞迴方法有個嚴重的bug,忽視了排序陣列這個關鍵的點。假設陣列為111111時,只需要判斷兩段端是否為相等,直接指標相減即可得到出現次數,而不需要繼續遞迴下去。當陣列為01111110時,這時則無法判斷左右兩側出現的1的次數,這時需要繼續向下遞迴,統計出現次數。
再次優化後的程式碼:

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
       
        if(array == null || array.length < 1){
            return 0;
        }
        int left = 0;
        int right = array.length - 1;
        while(right >= left){
            int mid = (left+right)/2;
            if(array[mid]<k){
                left = mid+1;
            }else if(array[mid] > k){
                right = mid-1;
            }else{
               int leftnum =  array[left] == k? mid-left+1 : findNumOfK(array,left,mid,k);
                int rightnum = array[right] == k? right-mid : findNumOfK(array,mid+1,right,k);
                return leftnum+rightnum;
            }
        }
        return 0;
    }
    public int findNumOfK(int[] array, int start, int end , int k){
        while(end >= start){
            int mid = (start+end)/2;
            if(array[mid]<k){
                start= mid+1;
            }else if(array[mid] > k){
                end = mid-1;
            }else{
                int leftnum =  array[start] == k? mid-start+1 : findNumOfK(array,start,mid,k);
                int rightnum = array[end] == k? end-mid : findNumOfK(array,mid+1,end,k);
                return leftnum+rightnum;
            }
        }
        return 0;
    }
}

以上均為本人的思路分析,不足之處希望大家批評指正。