一、二分查找的定义
二分查找,也称为二分搜索或折半搜索,是一种在一个有序数组中查找某一特定元素的搜索算法。其基本原理是将数组分成两个部分,然后确定目标元素可能存在于哪个部分,并继续在该部分中进行查找,直到找到目标元素或确定目标元素不存在为止。每次查找时,都将目标元素与数组的中间元素进行比较,如果相等则找到目标元素,如果目标元素小于中间元素,则目标元素可能在左半部分,否则可能在右半部分,然后递归或循环地在相应的半部分中继续进行查找。由于每次查找都将问题规模减半,因此二分查找的时间复杂度为O(log n),其中n为数组的大小。
二、二分查找的使用场景
二分查找的使用场景包括但不限于以下几种:
1. 已经排序的数组或列表:二分查找适用于已经排序的数组或列表,可以快速定位特定元素。
2. 查找特定值:如果需要查找数组中是否存在某个特定的值,可以使用二分查找来快速判断。
3. 查找边界值:对于有序数组,可以使用二分查找来查找某个值的第一个出现位置或最后一个出现位置。
4. 近似查找:如果要查找的值不存在,可以使用二分查找来找到离它最近的值。
5. 分治算法:二分查找是分治算法的一种基本思想,可以应用于解决一些分治问题。
三、二分查找中的区间混淆问题,二分查找中区间定义的两种方式
二分查找的逻辑虽然比较简单,但是涉及很多边界条件,很容易写错。例如while(left < right) 、while (left <=right)两种写法中right的赋值方式不同,有right = middle 和right = middle - 1两种。
1、二分查找区间定义第一种方式
定义target在一个左闭右闭的区间里,也就是[left , right]。
// 二分查找算法
public int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
// 区间定义方式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 {
right = middle - 1;
}
}
return -1; // 未找到target
}
2、二分查找区间定义的第二种方式
定义target在一个左闭右开的区间里,也就是[left ,right)。
// 二分查找算法
public int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
// 区间定义方式2:左闭右开
while (left < right) {
int middle = left + (right - left) / 2;
if (nums[middle] == target) {
return middle;
} else if (nums[middle] < target) {
left = middle + 1;
} else {
right = middle;
}
}
return -1; // 未找到target
}
这两种区间定义方式在实现中的不同之处在于对right的更新方式。
在左闭右开的区间定义方式中,如果nums[middle]不等于target,那么更新right为middle,即right = middle。这是因为当nums[middle]不等于target时,可以确定target在[left, middle)的区间内。由于右边界是开区间,所以此时不需要将middle位置上的元素纳入下一轮的查找范围。
而在左闭右闭的区间定义方式中,如果nums[middle]不等于target,那么更新right为middle - 1,即right = middle - 1。这是因为当nums[middle]不等于target时,可以确定target在[left, middle - 1]的区间内。由于右边界是闭区间,所以此时仍然需要将middle位置上的元素纳入下一轮的查找范围。
这两种区间定义方式的选择主要取决于具体问题的需求和约束。有些问题可能要求返回的区间是左闭右开的,而有些问题可能要求返回的区间是左闭右闭的。在实际应用中,根据具体情况选择合适的区间定义方式即可。