二分查找、二分边界查找算法的模板代码总结

前言

1. 待查找的数组有序或者部分有序
2. 要求时间复杂度低于O(n)，或者直接要求时间复杂度为O(log n)

1. 标准的二分查找
2. 二分查找左边界
3. 二分查找右边界
4. 二分查找左右边界
5. 二分查找极值点

标准二分查找

class BinarySearch {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + ((right - left) >> 1);
if (nums[mid] == target) return mid;
else if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
}
• 循环条件： left <= right
• 中间位置计算： mid = left + ((right -left) >> 1)
• 左边界更新：left = mid + 1
• 右边界更新： right = mid - 1
• 返回值： mid / -1

1. 我们的循环条件中包含了 left == right的情况，则我们必须在每次循环中改变 leftright的指向，以防止进入死循环
2. 循环终止的条件包括：

• 找到了目标值
• left > right （这种情况发生于当left, mid, right指向同一个数时，这个数还不是目标值，则整个查找结束。）
3. left + ((right -left) >> 1) 其实和 (left + right) / 2是等价的，这样写的目的一个是为了防止 (left + right)出现溢出，一个是用右移操作替代除法提升性能。
4. left + ((right -left) >> 1) 对于目标区域长度为奇数而言，是处于正中间的，对于长度为偶数而言，是中间偏左的。因此左右边界相遇时，只会是以下两种情况：

• left/mid , right (left, mid 指向同一个数，right指向它的下一个数)
• left/mid/right （left, mid, right 指向同一个数）

实战1：Guess Number Higher or Lower

We are playing the Guess Game. The game is as follows:

I pick a number from 1 to n. You have to guess which number I picked.

Every time you guess wrong, I'll tell you whether the number is higher or lower.

You call a pre-defined API guess(int num) which returns 3 possible results (-1, 1, or 0):

-1 : My number is lower
1 : My number is higher
0 : Congrats! You got it!
Example :

Input: n = 10, pick = 6
Output: 6

public class Solution extends GuessGame {
public int guessNumber(int n) {
int left = 1;
int right = n;
while (left <= right) {
int mid = left + ((right - left) >> 1);
if (guess(mid) == 0) {
return mid;
} else if (guess(mid) == -1) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
}

实战2：Sqrt(x)

Implement int sqrt(int x).
Compute and return the square root of x, where x is guaranteed to be a non-negative integer.
Since the return type is an integer, the decimal digits are truncated and only the integer part of the result is returned.

class Solution {
public int mySqrt(int x) {
if (x <= 1) return x;
int left = 1;
int right = x - 1;
while (left <= right) {
int mid = left + ((right - left) >> 1);
if (mid > x / mid) {
right = mid - 1;
} else if (mid < x / mid) {
if (mid + 1 > x / (mid + 1)) return mid;
left = mid + 1;
} else {
return mid;
}
}
return -1; // only for return a value
}
}

二分查找左边界

1. 数组有序，但包含重复元素
2. 数组部分有序，且不包含重复元素
3. 数组部分有序，且包含重复元素

左边界查找类型1

class Solution {
public int search(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;
}
}
return nums[left] == target ? left : -1;
}
}
• 循环条件： left < right
• 中间位置计算： mid = left + ((right -left) >> 1)
• 左边界更新：left = mid + 1
• 右边界更新： right = mid
• 返回值： nums[left] == target ? left : -1

左边界查找类型2

class Solution {
public int search(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 if (nums[mid] > target) {
right = mid;
} else {
right--;
}
}
return nums[left] == target ? left : -1;
}
}

[false, false, false, ..., fasle, true, true, ..., true]

public class Solution extends VersionControl {
int left = 0;
int right = n - 1;
while (left < right) {
int mid = left + ((right - left) >> 1);
left = mid + 1;
} else {
right = mid;
}
}
return isBadVersion(left + 1) ? left + 1 : -1;
}
}

实战4：Find Minimum in Rotated Sorted Array

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).

Find the minimum element.

You may assume no duplicate exists in the array.

class Solution {
public int findMin(int[] nums) {
if (nums.length == 1) return nums[0];
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + ((right - left) >> 1);
if (nums[mid] > nums[nums.length - 1]) {
left = mid + 1;
} else {
right = mid;
}
}
return nums[left];
}
}

实战5：Find Minimum in Rotated Sorted Array II

class Solution {
public int findMin(int[] nums) {
if (nums.length == 1) return nums[0];
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + ((right - left) >> 1);
if (nums[mid] > nums[right]) { // mid 位于旋转点左侧
left = mid + 1;
} else if (nums[mid] < nums[right]) { // mid 位于旋转点右侧
right = mid;
} else {
// 注意相等的时候的特殊处理，因为要向左查找左边界，所以直接收缩右边界
right--;
}
}
return nums[left];
}
}

二分查找右边界

class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + ((right - left) >> 1) + 1;
if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid;
}
}
return nums[right] == target ? right : -1;
}
}
• 循环条件： left < right
• 中间位置计算： mid = left + ((right -left) >> 1) + 1
• 左边界更新：left = mid
• 右边界更新： right = mid - 1
• 返回值： nums[right] == target ? right : -1

二分查找左右边界

实战6: Find First and Last Position of Element in Sorted Array

Given an array of integers nums sorted in ascending order, find the starting and ending position of a given target value.
Your algorithm's runtime complexity must be in the order of O(log n).
If the target is not found in the array, return [-1, -1].

class Solution {
public int[] searchRange(int[] nums, int target) {
int[] res = new int[]{-1, -1};
if(nums == null || nums.length == 0) return res;
// find the left-end
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + ((right - left) >> 1);
if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid;
}
}
res[0] = nums[left] == target ? left : -1;

// find right-end
if (res[0] != -1) {
if (left == nums.length - 1 || nums[left + 1] != target) {
res[1] = left;
} else {
right = nums.length - 1;
while (left < right) {
int mid = left + ((right - left) >> 1) + 1;
if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid;
}
}
res[1] = right;
}
}
return res;
}
}

二分查找极值

实战7：Find Peak Element

A peak element is an element that is greater than its neighbors.
Given an input array nums, where nums[i] ≠ nums[i+1], find a peak element and return its index.
The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.
You may imagine that nums[-1] = nums[n] = -∞.

class Solution {
public int findPeakElement(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + ((right - left) >> 1);
if (nums[mid] < nums[mid + 1]) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
}

总结

(完)

ChiuCheng

Talk is cheap, show me the code！

1.1k 声望
586 粉丝
0 条评论