java 高效方法實現二分法
阿新 • • 發佈:2020-12-20
題目要求:請實現有重複數字的有序陣列的二分查詢。
輸出在陣列中第一個大於等於查詢值的位置,如果陣列中不存在這樣的數,則輸出陣列長度加一。
先考慮這樣一個問題:對於一個有序陣列來說,什麼情況下是不存第一個大於等於 v的位置呢?
即:陣列中的所有數都比 v 小,可以寫成
if(a[n-1]<v)
return n+1;
判完這個條件以後,aa 數組裡就一定可以存在一個位置是答案了。這樣的話,我們來設答案在閉區間[Left,right][Left,right]之間
當這個區間只剩下一個數,即LeftrightLeftright的時候,就是我們答案的位置了。
下面我們用二分的方式對這個區間進行縮減
每次拿出中間的值 a[Mid]a[Mid] 和 vv 比較,那會有三種結果:大於、等於、小於
1、如果a[Mid] \gt va[Mid]>v
說明說明後面的那段都不可能是結果了。因為a[Mid]就是一個大於等於v的值了,後面的肯定都不可能是第一個。這樣的話,可能的答案區間就縮減成了[Left,Mid][Left,Mid]
即
Right = Mid;
2、如果a[Mid] = va[Mid]=v
這種情況和1相同
3、如果a[Mid] \lt va[Mid]<v
那說明,a[Mid]a[Mid]以及a[Mid]a[Mid]之前的都不可能是我們的結果,可能的答案區間縮減成了[Mid+1,Right][Mid+1,Right]。
Left = Mid+1;
因為現在陣列中肯定有一個位置是我們要求的結果,所以在縮減區間的過程中不會出現Left>RightLeft>Right的情況。所以,當LeftrightLeftright的時候我們就找到答案了,這個答案就是Left(Right),還要注意一點,本題要輸出的位置是從1開始的,但是我們的區間是用的陣列下標,所以最後要輸出Left+1(Right+1)。
public class Dichotomy {
public static void main(String[] args) {
int[] arr = {1, 2, 4, 4 , 5};
int i = upperBound(5, 4, arr);
System.out.println(i);
}
/**
* 二分查詢
*
* @param n int整型 陣列長度
* @param v int整型 查詢值
* @param a int整型一維陣列 有序陣列
* @return int整型
*/
public static int upperBound(int n, int v, int[] a) {
if (a[n - 1] < v) {
return n + 1;
}//如果不存在這樣的數:即陣列中所有數字都比
int left = 0;
int right = n - 1;
while (left < right) {
int mid = left + (right - left) / 2;//防溢位
if (a[mid] >= v) right = mid;
else left = mid + 1;
}
return left + 1;
}
}