前言
使用 二分法 的前提:
- 数组必须是有序的
- 数组中元素不能重复
为什么不能重复?
如果数组中元素有重复的,那么查找出来的元素就会有多个。
二分法怎么用?
每次都取区间的中间值,来判断所查询的元素在哪一个区间中,每次进行二分的时候,都能舍去另一半的区间不用查找。
比如:我想要查找数组中有没有88,
middle = (left + right ) / 2
middle = (0 + 6) / 2 = 3
接着拿着 nums[middle] 和 target(查找的元素)比较,
如果 nums[middle] < target 说明在右区域,移动 left
如果 nums[middle] > target 说明在左区域,移动 right
思路
- 定义区间(很重要):是左闭右闭,还是左闭右开
你定义的区间决定了你的很多边界条件怎么写,例如到底是 while(left < right) 还是 while(left <= right),到底是right = middle呢,还是要right = middle - 1
如果是左闭右闭:
- while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
- if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
class Solution {
// 区间的定义:左闭右闭[]
public int search(int[] nums, int target) {
int left = 0;
// 右边封闭,需要包含元素
int right = nums.length - 1;
while (left <= right) {
int middle = (left + right) / 2;
if (nums[middle] == target) {
return middle;
} else if (nums[middle] < target) {
left = middle + 1;
}else {
right = middle - 1;
}
}
// 没有找到
return -1;
}
}
如果是左闭右开:
- while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
- if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
class Solution {
// 区间的定义:左闭右开[)
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length;
while (left < right) {
int middle = (left + right) / 2;
// target 在右区域
if (nums[middle] < target) {
left = middle + 1;
}
// 在 左区域
else if (nums[middle] > target) {
right = middle;
}else {
return middle;
}
}
// 循环结束,没有找到
return -1;
}
}