- 二叉查找的前提条件:有序 数组
- 为了对于在有序链表中也可用“二分查找” — 有了跳表这个数据结构
- 解题方法:递归、循环
- 写二分查找的题目精髓就是:搜索区间 记心里!!
一、一道简单题
- 有序数组
- 无重复元素
- 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。【寻找一个数】
public int search(int[] nums, int target) {
//搜索区间为左闭右闭:[left, right]
int left = 0, right = nums.length - 1;
//当搜索区间为空时,跳出循环
while(left <= right) {
int mid = left + (right - left) / 2;
if(nums[mid] == target) {
return mid;
}
else if(nums[mid] < target) {
left = mid + 1;
}
else if(nums[mid] > target) {
right = mid - 1;
}
}
return -1;
}
二、四道变形题
- 有序数组中有重复元素
- 图片摘自[极客时间-徐争老师的数据结构与算法之美的课]
2.1 在有重复元素的有序数组中查找第一个值等于给定值的元素(在排序数组中查找元素的第一个位置)
法一:
找最左位置:由右逼近
public int search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while(left <= right) {
int mid = left + (right - left) / 2;
if(nums[mid] > target) {
right = mid - 1;
}
else if(nums[mid] < target) {
left = mid + 1;
}
//进一步缩小搜索区间
else if(nums[mid] == target) {
right = mid - 1;
}
}
// 最后要检查 left 越界的情况
if(left <= nums.length - 1 && nums[left] == target) {
return left;
}
else {
return -1;
}
}
法二:
public int bsearch(int[] a, int n, int value) {
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (a[mid] > value) {
high = mid - 1;
} else if (a[mid] < value) {
low = mid + 1;
} else {
if ((mid == 0) || (a[mid - 1] != value)) return mid;
else high = mid - 1;
}
}
return -1;
}
2.2 在有重复元素的有序数组中查找最后一个值等于给定值的元素(在排序数组中查找元素的最后一个位置)
法一:
找最右位置:由左逼近
public int searchRight(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while(left <= right) {
int mid = left + (right - left) / 2;
if(nums[mid] > target) {
right = mid - 1;
}
else if(nums[mid] < target) {
left = mid + 1;
}
//向右缩小区间
else if(nums[mid] == target) {
left = mid + 1;
}
}
if(right >= 0 && nums[right] == target) {
return right;
}
else {
return -1;
}
}
法二:
public int bsearch(int[] a, int n, int value) {
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (a[mid] > value) {
high = mid - 1;
} else if (a[mid] < value) {
low = mid + 1;
} else {
if ((mid == n - 1) || (a[mid + 1] != value)) return mid;
else low = mid + 1;
}
}
return -1;
}
2.3 在有重复元素的有序数组中查找第一个大于等于给定值的元素
在有序数组中,查找第一个大于等于给定值的元素。
比如,数组中存储的这样一个序列:3,4,6,7,10。
查找第一个大于等于 5 的元素,那就是 6。
public int bsearch(int[] a, int n, int value) {
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (a[mid] >= value) {
if ((mid == 0) || (a[mid - 1] < value)) return mid;
else high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
2.4 在有重复元素的有序数组中查找最后一个小于等于给定值的元素
public int bsearch7(int[] a, int n, int value) {
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (a[mid] > value) {
high = mid - 1;
} else {
if ((mid == n - 1) || (a[mid + 1] > value)) return mid;
else low = mid + 1;
}
}
return -1;
}