二分查找常常用於有序數組的查找操作。當然如果是一個問題,數組的順序滿足特定條件(不僅僅是升序或者降序,也可以是先升後降或先降後升),就可以通過逐步排查,縮小問題的規模的方式找到,這種算法也是二分查找算法。
PS:不想看過程的,直接拉到萬能公式,即看即用!
兩種二分查找
第一種
第二種
從上面可以看出:
第二種方式有兩種邊界轉移方式:
left = mid + 1;
和right = mid;
這種不會產生死循環left = mid-1;
和right = mid;
這種會產生死循環(循環條件一直成立),此時需要把mid向上取整。
思維導圖
萬能公式
int search(int[] nums, int target, int l, int r) {
while (l < r) {
int mid = l + (r - l) / 2;
// if (符合條件) return mid;
if (target在右區間) {
l = mid + 1;
} else {
r = mid;
}
}
if (符合條件) return l;
else return -1;
}
適用於:
主要注意的幾個點:
- while繼續循環的條件:
left<right;
注意不要帶等於號,while退出循環的時候,一定是left==right,所以不需要額外的考慮用left還是right去判斷條件。 - 取中位數的時候,防止left和right過大,以至於left+right超過int類型上限,可以用
mid = left + (right - left) / 2;
來求中位數。 - 在while中,如果題目說明了沒有重複元素的話,那麼萬能公式中註釋的if條件可以去掉註釋,以便提前跳出條件,如果存在相同的元素讓輸出最左邊或者最右邊的元素,那麼這個if條件就不能加
- 賦值left和right邊界的時候,需要注意left一定要先賦值,跟快排需要先從右邊一樣。
代碼示例(套公式示例)
LeetCode704
【題目】
給定一個 n 個元素有序的(升序)整型數組 nums 和一個目標值 target ,寫一個函數搜索 nums 中的 target,如果目標值存在返回下標,否則返回 -1。
【示例】
輸入: nums = [-1,0,3,5,9,12], target = 9
輸出: 4
解釋: 9 出現在 nums 中並且下標爲 4
【代碼】
public int search(int[] nums, int target) {
if (nums==null || nums.length==0)
return -1;
int left,right,mid;
left = 0;
right = nums.length-1;
while (left < right) {
mid = left + (right - left) / 2;
// if (nums[mid]==target)
// return mid;
if (nums[mid]<target)
left = mid+1;
else
right = mid;
}
// 由於條件可能不存在,所以需要額外判斷
if (target==nums[left])
return left;
else return -1;
}
LeetCode35
【題目】
給定一個排序數組和一個目標值,在數組中找到目標值,並返回其索引。如果目標值不存在於數組中,返回它將會被按順序插入的位置。
你可以假設數組中無重複元素。
【示例】
輸入: [1,3,5,6], 5
輸出: 2
輸入: [1,3,5,6], 2
輸出: 1
輸入: [1,3,5,6], 0
輸出: 0
輸入: [1,3,5,6], 7
輸出: 4
【代碼】
public int searchInsert(int[] nums, int target) {
if (nums==null || nums.length==0)
return 0;
int left,right,mid;
left = 0;
right = nums.length-1;
// 最大值的情況需要額外判斷
if (target>nums[right]) return right+1;
while (left < right) {
mid = left + (right - left) / 2;
// if (nums[mid]==target)
// return mid;
if (nums[mid]<target)
left = mid+1;
else
right = mid;
}
// 由於條件一定成立,所以直接輸出即可
return left;
}
LeetCode1095
【題目】
給你一個 山脈數組 mountainArr,請你返回能夠使得 mountainArr.get(index) 等於 target 最小 的下標 index 值。
如果不存在這樣的下標 index,就請返回 -1。
何爲山脈數組?如果數組 A 是一個山脈數組的話,那它滿足如下條件:
首先,A.length >= 3
其次,在 0 < i < A.length - 1 條件下,存在 i 使得:
- A[0] < A[1] < … A[i-1] < A[i]
- A[i] > A[i+1] > … > A[A.length - 1]
你將 不能直接訪問該山脈數組,必須通過 MountainArray 接口來獲取數據:
- MountainArray.get(k) - 會返回數組中索引爲 k 的元素(下標從 0 開始)
- MountainArray.length() - 會返回該數組的長度
【示例】
輸入:array = [1,2,3,4,5,3,1], target = 3
輸出:2
解釋:3 在數組中出現了兩次,下標分別爲 2 和 5,我們返回最小的下標 2。
輸入:array = [0,1,2,4,2,1], target = 3
輸出:-1
解釋:3 在數組中沒有出現,返回 -1。
【思考】
-
先找到山頂元素 mountaintop 所在的索引。
-
在前有序且升序數組中找 target 所在的索引,如果找到了,就返回,如果沒有找到,就執行第 3 步;
-
如果步驟 2 找不到,就在後有序且降序數組中找 target 所在的索引。
所以問題的關鍵就是如何在一個先升後降的數組中找到最高的一個。
// 尋找峯頂的代碼
public int getTop(MountainArray mountainArr) {
int left,right,mid,midVal,midAf;
left = 0;
right = mountainArr.length()-1;
while (left < right) {
mid = left + (right - left) / 2;
midVal = mountainArr.get(mid);
midAf = mountainArr.get(mid+1);
if (midAf > midVal)// 如果當前的數比右邊的數小,它一定不是山頂
left = mid + 1;
else
right = mid;
}
return left;
}
如果當前的數比右邊的數小,它一定不是山頂,又由於left和right的二分是mid在左區域(看上圖),所以最後跳出的時候,只有兩個元素,mid在左邊,left和right同時指向右邊,右邊就是那個峯頂。
【全部代碼】
/**
* // This is MountainArray's API interface.
* // You should not implement it, or speculate about its implementation
* interface MountainArray {
* public int get(int index) {}
* public int length() {}
* }
*/
class Solution {
public int findInMountainArray(int target, MountainArray mountainArr) {
if (mountainArr==null || mountainArr.length()==0)
return -1;
int l,top,leftIndex;
top = getTop(mountainArr);
l = 0;
leftIndex = getAsc(mountainArr,target,l,top);
if (leftIndex != -1)
return leftIndex;
else
return getDesc(mountainArr,target,top,mountainArr.length()-1);
}
/**
* 求得山峯的峯頂元素
* */
public int getTop(MountainArray mountainArr) {
int left,right,mid,midVal,midAf;
left = 0;
right = mountainArr.length()-1;
while (left < right) {
mid = left + (right - left) / 2;
midVal = mountainArr.get(mid);
midAf = mountainArr.get(mid+1);
if (midAf > midVal)
left = mid + 1;
else
right = mid;
}
return left;
}
/**
* 左邊升序求指定元素
* */
public int getAsc(MountainArray mountainArr, int target, int l ,int r) {
while (l < r) {
int mid = l + (r - l) / 2;
if (mountainArr.get(mid) < target)
l = mid + 1;
else r = mid;
}
if (mountainArr.get(l) == target)
return l;
else return -1;
}
/**
* 右邊降序求指定元素
* */
public int getDesc(MountainArray mountainArr, int target, int l ,int r) {
while (l < r) {
int mid = l + (r - l) / 2;
if (mountainArr.get(mid) > target)
l = mid + 1;
else r = mid;
}
if (mountainArr.get(l) == target)
return l;
else return -1;
}
}
// 用於本地測試,提交的時候可以不寫以下代碼
class MountainArray {
ArrayList<Integer> table;
public MountainArray() {
table = new ArrayList();
}
public int get(int index) {
return table.get(index);
}
public void add(int val) {
table.add(val);
}
public void addAll(int[] a) {
for (int i=0; i<a.length; i++) {
table.add(a[i]);
}
}
public int length() {
return table.size();
}
}
【Java 面試那點事】
這裏致力於分享 Java 面試路上的各種知識,無論是技術還是經驗,你需要的這裏都有!
這裏可以讓你【快速瞭解 Java 相關知識】,並且【短時間在面試方面有跨越式提升】
面試路上,你不孤單!