1.說明
前面介紹了二分查找代碼框架,
查找的都是與target相同的數,
本文介紹的二分查找變體,
查找的是正好大於或者小於target的數,
即要找的數target不存在,
希望返回滿足一定要求的數。
2.查找正好大於target的元素
這是二分查找代碼框架中,
查找左側邊界代碼框架的變體,
當target不存在時,
得到的索引恰好是比target大的最小元素的索引,
即nums數組中第1個大於target的元素索引。
public static int leftBound(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
// 下面的兩個判斷可省略,最終結果會覆蓋這兩種情況
// target比最小的元素還小,那索引爲0的元素正好比target大
if (target < nums[left]) {
return 0;
}
// target比最大的元素還大,則-1表示沒有比target大的元素了
if (target > nums[right]) {
return -1;
}
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] == target) {
// 不能返回,鎖定左邊界,繼續縮小右邊界
right = mid - 1;
}
// 上面的"right = mid - 1;"代碼體相同,
// 判斷條件可以合併爲else if (nums[mid] >= target)
}
return left;
}
3.查找正好小於target的元素
這是二分查找代碼框架中,
查找右側邊界代碼框架的變體,
當target不存在時,
得到的索引恰好是比target小的最大元素的索引,
即nums數組中最後一個小於target的元素索引。
public static int rightBound(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
// 下面的兩個判斷可省略,最終結果會覆蓋這兩種情況
// target比最小的元素還小,則-1表示沒有比target小的元素了
if (target < nums[left]) {
return -1;
}
// target比最大的元素還大,那索引爲nums.length-1的元素正好比target小
if (target > nums[right]) {
return right;
}
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] == target) {
// 不能返回,鎖定右邊界,繼續縮小左邊界
left = mid + 1;
}
// 上面的"left = mid + 1;"代碼體相同,
// 判斷條件可以合併爲else if (nums[mid] <= target)
}
return right;
}
4.二分查找(變體)代碼框架優化
如下代碼優化後非常簡潔,
適合在實際項目中使用,
但是不推薦記憶下面的代碼框架,
因爲合併了很多實現細節,
不利於分析和理解,
還是推薦記憶上面的原始版本。
4.1.查找左側邊界變體優化
public static int leftBoundAdvance(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
4.2.查找右側邊界變體優化
public static int rightBoundAdvnce(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right;
}