# [算法总结] 二分查找

### 1. 最基本的二分查找

``````public int binarySearch(int[] A, int target, int n){
int low = 0, high = n, mid;
while(low <= high){
mid = low + (high - low) / 2;
if(A[mid] == target){
return mid;
}else if(A[mid] > target){
high = mid - 1;
}else{
low = mid + 1;
}
}
return -1;
}
``````

1. 循环的判定条件是：`low <= high`
2. 为了防止数值溢出，`mid = low + (high - low)/2`
3. `A[mid]`不等于`target`时，`high = mid - 1``low = mid + 1`

leetcode参考：Search Insert Position

### 2. 查找目标值区域的左边界/查找与目标值相等的第一个位置/查找第一个不小于目标值数的位置

A = [1,3,3,5, 7 ,7,7,7,8,14,14]
target = 7
return 4

``````public int binarySearchLowerBound(int[] A, int target, int n){
int low = 0, high = n, mid;
while(low <= high){
mid = low + (high - low) / 2;
if(target <= A[mid]){
high = mid - 1;
}else{
low = mid + 1;
}
}
if(low < A.length && A[low] == target)
return low;
else
return -1;
}
``````

### 3. 查找目标值区域的右边界/查找与目标值相等的最后一个位置/查找最后一个不大于目标值数的位置

A = [1,3,3,5,7,7,7, 7 ,8,14,14]
target = 7
return 7

``````public int binarySearchUpperBound(int[] A, int target, int n){
int low = 0, high = n, mid;
while(low <= high){
mid = low + (high - low) / 2;
if(target >= A[mid]){
low = mid + 1;
}else{
high = mid - 1;
}
}
if(high >= 0 && A[high] == target)
return high;
else
return -1;
}
``````

### 4. 查找最后一个小于目标值的数/查找比目标值小但是最接近目标值的数

A = [1,3,3, 5 ,7,7,7,7,8,14,14]
target = 7
return 3

``````int low = 0, high = n, mid;
while(low <= high){
mid = low + (high - low) / 2;
if(target <= A[mid]){
high = mid - 1;
}else{
low = mid + 1;
}
}
return high < 0 ? -1 : high;
``````

### 5. 查找第一个大于目标值的数/查找比目标值大但是最接近目标值的数

A = [1,3,3,5,7,7,7,7, 8 ,14,14]
target = 7
return 8

``````int low = 0, high = n, mid;
while(low <= high){
mid = low + (high - low) / 2;
if(target >= A[mid]){
low = mid + 1;
}else{
high = mid - 1;
}
}
return low > n ? -1 : low;
``````

### 6. 旋转数组返回最小元素

#### 6.1 查找旋转数组的最小元素（假设不存在重复数字）

LeetCode: Find Minimum in Rotated Sorted Array
Input: [3,4,5,1,2]
Output: 1

``````public int findMin(int[] nums) {
int len = nums.length;
if(len == 0)
return -1;
int left = 0, right = len - 1, mid;

while(left < right){
mid = left + (right - left) / 2;
if(nums[mid] > nums[right])
left = mid + 1;
else{
right = mid;
}
}
return nums[left];
}
``````

1. 循环判定条件为`left < right`，没有等于号
2. 循环中，通过比较nums[left]与num[mid]的值来判断mid所在的位置:
• 如果`nums[mid] > nums[right]`，说明前半部分是有序的，最小值在后半部分，令`left = mid + 1`
• 如果`nums[mid] <= num[right]`，说明最小值在前半部分，令`right = mid`

#### 6.2 查找旋转数组的最小元素（存在重复项）

LeetCode: Find Minimum in Rotated Sorted Array II

Input: [2,2,2,0,1]
Output: 0

``````public int findMin(int[] nums) {
int len = nums.length;
if(len == 0)
return -1;
int left = 0, right = len - 1, mid;
while(left < right){
mid = left + (right - left) / 2;
if(nums[mid] > nums[right])
left = mid + 1;
else if(nums[mid] < nums[right])
right = mid;
else
right--;
}
return nums[left];
}
``````

### 7. 在旋转排序数组中搜索

#### 7.1 不考虑重复项

LeetCode: Search in Rotated Sorted Array

• 先利用方法 6.1 查找数组中的最小元素，即确定分界点的位置
• 把旋转的数组当成偏移，用`(offset + mid) % len`来求真实的 mid 的位置。
• 然后用二分查找来定位目标值
``````public int search(int[] nums, int target) {
int len = nums.length;
if(len == 0)
return -1;
int left = 0, right = len - 1, mid;
while(left < right){
mid = left + (right - left) / 2;
if(nums[mid] > nums[right])
left = mid + 1;
else
right = mid;
}
int offset = left;
left = 0;
right = len - 1;
while(left <= right){
mid = left + (right - left) / 2;
int realmid = (mid + offset) % len;
if(nums[realmid] == target)
return realmid;
else if(nums[realmid] < target)
left = mid + 1;
else
right = mid - 1;
}
return -1;
}
``````

``````public int search(int[] nums, int target) {
int len = nums.length;
if(len == 0)
return -1;
int left = 0, right = len - 1, mid;
while(left <= right){
mid = left + (right - left) / 2;
if(nums[mid] == target)
return mid;
else if(nums[left] <= nums[mid]){
if(target < nums[mid] && target >= nums[left])
right = mid - 1;
else
left = mid + 1;
}else if(nums[mid] <= nums[right]){
if(target > nums[mid] && target <= nums[right])
left = mid + 1;
else
right = mid - 1;
}
}
return -1;
}
``````

#### 7.2 存在重复项

LeetCode: Search in Rotated Sorted Array II

``````public boolean search(int[] nums, int target) {
int len = nums.length;
if(len == 0)
return false;
int left = 0, right = len - 1, mid;
while(left <= right){
mid = left + (right - left) / 2;
if(nums[mid] == target)
return true;
else if(nums[mid] > nums[right]){
if(target < nums[mid] && target >= nums[left])
right = mid;
else
left = mid + 1;
}else if(nums[mid] < nums[right]){
if(target > nums[mid] && target <= nums[right])
left = mid + 1;
else
right = mid;
}else{
right --;
}
}
return false;
}
``````

### 8. 二维数组中的查找

• 当要查找数字比右上角数字大时，下移；
• 当要查找数字比右上角数字小时，左移；