劍指Offer - 數字在排序陣列中出現的次數(Java實現)
阿新 • • 發佈:2019-01-02
題目描述:
統計一個數字在排序陣列中出現的次數
思路分析:
已下程式碼均通過牛客測試,主要工作只是在時間複雜度上的優化。
看到排序陣列,然後又看到查詢。首先就想到二分查詢,開始的思路是先通過二分查詢找出需要統計的那個數字,找到後再從那個位置依次向後遍歷。程式碼如下:
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; } }
以上均為本人的思路分析,不足之處希望大家批評指正。