劍指Offer-39-數字在排序陣列中出現的次數
題目
統計一個數字在排序陣列中出現的次數。
解析
預備知識
在排序陣列中,高效的查詢指定數字可以採用二分查詢,該方法的複雜度為O(logn),它可以看做有序陣列中查詢的標配!思路如下:
1. 首先有個指標start,end分別指向陣列的開頭和結尾,k為待查數字。
2. 判斷start是否大於end,若是則結束,否則進行第三步
3. 求出中間索引mid = start + ((end - start) >> 1)
4. 判斷mid所指向的值與k比較,若大於,則說明k位於mid前面,所以更新end = mid - 1
,跳到第2步;若小於則說明,k位於mid後面,所以更新start = mid + 1
,跳到第2步;若相等,則說明找到,直接返回當前mid索引即可
private static int binarySearch(int[] array, int k) {
int start = 0, end = array.length - 1;
while(start <= end) {
int mid = start + ((end - start) >> 1);
if(array[mid] < k) {
start = mid + 1;
} else if(array[mid] > k) {
end = mid + 1;
} else {
return mid;
}
}
return -1;
}
思路一
既然我們要統計指定數字在陣列出現的次數,我們先根據二分查詢找到該數字的位置。因為在排序陣列中,相同的數字是連續在一起的,所以對得到的位置向前和向後搜查並統計即可。
public static int GetNumberOfK3(int [] array , int k) {
if(array == null || array.length == 0) {
return 0;
}
int index = binarySearch(array, k);
int count = 0;
if(index != - 1) {
count++;
//向前搜查
for(int i = index - 1; i >= 0; i--) {
if(array[i] == k) {
count++;
}
}
//向後搜查
for(int i = index + 1; i < array.length; i++) {
if(array[i] == k) {
count++;
}
}
}
return count;
}
思路二
但是思路一有個弊端,我們雖然利用高效的二分找到該數字的任意的一個位置,但是如果連續的數字有很多,演算法複雜度退化為O(n),顯然是不能接受。我們發現只要確定了數字出現的第一次的位置和最後一次的位置,做差即可確定其出現的總次數了。所以問題轉化為如何求數字第一次出現的位置和最後一次的位置。
求數字第一次出現的位置:改變二分查詢
對於經典二分查詢中,當mid所指的內容等於給定的值的時候,我們令end = mid - 1
;這樣可以進一步向前縮小搜查的範圍,因為當前的搜查到的位置可能不是第一次出現,所以要繼續向該位置之前查詢。而最後結束迴圈的時候,start指向的位置就是數字第一次出現的位置或者為剛剛大於該值的位置(這種情況是陣列中不存在該值)。
/**
* 變形的二分查詢
* @param array
* @param k
* @return start指向k第一次出現的位置,或者指向剛剛大於k的位置(不存在k)
*/
public static int findFirst(int[] array, int k) {
int start = 0, end = array.length - 1;
while(start <= end) {
int mid = start + ((end - start) >> 1);
if(array[mid] < k) {
start = mid + 1;
} else {
end = mid - 1;
}
}
return start;
}
求數字最後一次出現的位置:改變二分查詢
對於經典二分查詢中,當mid所指的內容等於給定的值的時候,我們令start = mid + 1
;這樣可以進一步向前縮小搜查的範圍,因為當前的搜查到的位置可能不是最後一次出現,所以要繼續向該位置之後查詢。而最後結束迴圈的時候,end指向的位置就是數字最後一次出現的位置或者為剛剛小於該值的位置(這種情況是陣列中不存在該值)。
/**
* 變形的二分查詢
* @param array
* @param k
* @return end指向k最後一次出現的位置,或者指向剛剛小於k的位置(不存在k)
*/
public static int findLast(int[] array, int k) {
int start = 0, end = array.length - 1;
while(start <= end) {
int mid = start + ((end - start) >> 1);
if(array[mid] > k) {
end = mid - 1;
} else {
start = mid + 1;
}
}
return end;
}
等於得到的first和last索引後,我們取差值 + 1即可得到出現的次數。
1. 比如若存在2個k,first位置為2,last位置為3,那麼次數為:3 - 2 + 1 = 2
2. 比如不存在k, first位置為3, last位置為2, 那麼次數為:2 - 3 + 1 = 0
顯然,k是否存在都符合差值 + 1來求長度。
public static int GetNumberOfK2(int [] array , int k) {
if(array == null || array.length == 0) {
return 0;
}
int firstOcur = findFirst(array, k);
int lastOcur = findLast(array, k);
return lastOcur - firstOcur + 1;
}
總結
有序陣列的查詢首選二分查詢。
相關文章
- 【劍指offer】數字在排序陣列中出現的次數排序陣列
- 《劍指offer》:[38]數字在排序陣列中出現的次數排序陣列
- 劍指OFFER-數字在升序陣列中出現的次數(Java)陣列Java
- 九度 1349 數字在排序陣列中出現的次數排序陣列
- JZ-037-數字在排序陣列中出現的次數排序陣列
- [劍指offer題解][Java]陣列中出現次數超過一半的數字Java陣列
- 力扣 - 劍指 Offer 39. 陣列中出現次數超過一半的數字力扣陣列
- Leetcode 劍指 Offer 39. 陣列中出現次數超過一半的數字LeetCode陣列
- 劍指 Offer 56 - I. 陣列中數字出現的次數陣列
- 找到陣列中出現特定次數數字的問題陣列
- 劍指offer 陣列中只出現一次的數字陣列
- 陣列中出現兩次的數陣列
- 陣列中出現次數超過一半的數字陣列
- 《劍指offer》:[40]陣列中只出現一次的數字陣列
- 【劍指offer】陣列中只出現一次的數字(1)陣列
- 【劍指offer】陣列中只出現一次的數字(2)陣列
- 48 陣列中出現次數超過一半的數字陣列
- 求出陣列中出現次數大於一半的數字陣列
- 查詢陣列中出現次數大於陣列長度一半的數字陣列
- 劍指offer:旋轉陣列的最小數字陣列
- 劍指offer 旋轉陣列的最小數字陣列
- Matlab tabulate統計數字出現的次數,如果陣列中出現0Matlab陣列
- 每日一題 - 劍指 Offer 53 - I. 在排序陣列中查詢數字 I每日一題排序陣列
- JZ-028-陣列中出現次數超過一半的數字陣列
- 每日一練(20):陣列中出現次數超過一半的數字陣列
- 劍指Offer--陣列中重複的數字陣列
- 劍指offer-轉陣列的最小數字-php陣列PHP
- 《劍指offer》:[51]陣列中的重複數字陣列
- js找出陣列中出現最多的元素和次數JS陣列
- 【劍指offer】7.旋轉陣列的最小數字陣列
- 劍指 Offer 11. 旋轉陣列的最小數字陣列
- 【劍指 Offer】11. 旋轉陣列的最小數字陣列
- 【劍指offer】把陣列排成最小的數陣列
- 劍指offer之列印超過陣列一半的數字陣列
- 統計陣列中各數字(元素)出現的次數陣列
- 計算陣列中每個數字出現的次數陣列
- Leetcode 劍指 Offer 03. 陣列中重複的數字LeetCode陣列
- 劍指offer刷題之路--1.陣列中重複的數字陣列