二分搜索
# 二分搜索
题目:
34. 在排序数组中查找元素的第一个和最后一个位置 (opens new window)
一般二分搜索都是用于有序的数组中,查找一个数,基本框架如下:
int binarySearch(int[] nums, int target) {
int left = 0, right = ...;
while(...) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
...
} else if (nums[mid] < target) {
left = ...
} else if (nums[mid] > target) {
right = ...
}
}
return ...;
}
计算 mid 时需要防止溢出,代码中 left + (right - left) / 2
就和 (left + right) / 2
的结果相同,但是有效防止了 left
和 right
太大直接相加导致溢出。
# 二分查找
题目:
思想:
此题是有序的升序数组,然后需要找到目标元素的下标,就是典型的二分查找,套用模板即可。
代码:
class Solution {
public int search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while(left <= right) {
int middle = left + (right - left) / 2;
if(nums[middle] == target) {
return middle;
} else if(nums[middle] < target) {
left = middle + 1;
} else if(nums[middle] > target) {
right = middle - 1;
}
}
return -1;
}
}
扩展:
现在给你有序数组 nums = [1,2,2,2,3]
,target
为 2,此算法返回的索引是 2,没错。但是如果我想得到 target
的左侧边界,即索引 1,或者我想得到 target
的右侧边界,即索引 3,这样的话此算法是无法处理的。
其实边界就是想让我们找到某一元素之后,再去找下一个元素,所以我们只需要根据是找左侧边界还是右侧边界,当我们找到目标元素之后,让我们的right
或者left
进行移动即可。
左边界:
int left_bound(int[] nums, int target) {
int left = 0, 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 - 1;
} else if (nums[mid] == target) {
// 别返回,锁定左侧边界
right = mid - 1;
}
}
// 最后要检查 left 越界的情况
if (left >= nums.length || nums[left] != target) {
return -1;
}
return left;
}
右边界
int right_bound(int[] nums, int target) {
int left = 0, 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 - 1;
} else if (nums[mid] == target) {
// 别返回,锁定右侧边界
left = mid + 1;
}
}
// 最后要检查 right 越界的情况
if (right < 0 || nums[right] != target) {
return -1;
}
return right;
}
越界情况:因为right是mid-1,而left是mid+1,所以可能存在right < 0,或者left >= nums.length。
# 在排序数组中查找元素的第一个和最后一个位置
题目:
思想:
这里就相当于我们需要找到目标元素的左边界
和右边界
,运用上题的拓展即可。
代码:
class Solution {
public int[] searchRange(int[] nums, int target) {
return new int[]{left_bound(nums, target), right_bound(nums, target)};
}
int left_bound(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 - 1;
} else if (nums[mid] == target) {
right = mid - 1;
}
}
if (left >= nums.length || nums[left] != target) {
return -1;
}
return left;
}
int right_bound(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 - 1;
} else if (nums[mid] == target) {
left = mid + 1;
}
}
if(right < 0 || nums[right] != target) {
return -1;
}
return right;
}
}
# 参考
上次更新: 2024/06/29, 15:13:44