LeetCode | 704 BinarySearch
https://github.com/dolphinmind/datastructure/tree/datastructure-array
思考
二分法在进行问题的拆解过程中,子问题与父问题性质相同,代码随想录-二分法引入开闭区间概念,底层核心是什么?
-
left = 0,0是索引的起始,在C系列语言,即左边界是闭合的;开闭与右边界索引选取的关联性最大;
-
若right = length,在C系列语言中nums[length]是超出索引范围之外,取不到,即这种[left,right),左闭右开区间。在进行mid判断的时候,即把问题区间一分为二,如果选取的是mid的左侧区间,为了保持子问题和父问题边界的逻辑一致性,即
左闭右开
,那么right = nums[mid] 这个值我取不到,就符合条件;倘若是mid的右侧区间,那么左侧边界值就一定要取到,那么必须使用 left = nums[mid+1] -
若right = length - 1,则是同理,父问题区间类型为
左闭右闭
,那么子问题也要保持边界的逻辑一致性
通过上面的分析,借由开闭区间这个思路,就可以判断循环逻辑判断的是否取等
[left, right] while(left <= right)
[left, right) while(left < right)
主类
package com.github.dolphinmind.array.binarysearch;
/**
* @author dolphinmind
* @ClassName BinarySearch
* @description 704 二分法搜索
* 前提条件:有序数组,且无重复元素
* 循环判断:专注索引
* 逻辑判断:专注值
* 特殊情况:无结果,返回-1
* 技巧:mid = left + ((right - left) >> 1)
* @date 2024/7/31
*/
public class BinarySearch {
private int left;
private int right;
private int mid;
/**
* 二分法搜索:左闭右闭区间
* 分析:循环条件看索引,中间索引采用移位,逻辑判断条件优先选取目标,循环不成立有兜底 -1
* @param nums
* @param target
* @return
*/
public int binarySearchRightClose(int[] nums, int target) {
left = 0;
right = nums.length - 1;
while (left <= right) {
mid = left + ((right - left) >> 1);
if (target == nums[mid]) {
return mid;
} else if (target > nums[mid]) {
left = mid + 1;
} else if (target < nums[mid]) {
right = mid;
}
}
return -1;
}
/**
* 二分法搜索:左闭右开区间
* @param nums
* @param target
* @return
*/
public int binarySearchRightOpen(int[] nums, int target) {
left = 0;
right = nums.length;
while (left < right) {
mid = left + ((right - left) >> 1);
if (target == nums[mid]) {
return mid;
} else if (target > nums[mid]) {
left = mid + 1;
} else if (target < nums[mid]) {
right = mid - 1;
}
}
return -1;
}
/**
* <p>
* 左右边界索引的二分查找,适用于查找左边界和右边界,但同样适应于数组中只存在一个target的情况
* 假设target = nums[mid]一开始就成立
* 尽管在第一次比较时,right或left可能看似"跳过了"目标值,
* 但由于target唯一性,后续的搜索范围更新操作不会导致left或right错过target
* 对于的就是左边界索引的left = mid + 1, 有边界索引的 right = mid - 1
* left 或 right都会正确地指向target的索引位置,成为有效的左边界或有边界索引
* </p>
*/
/**
* 前置条件:有序数组,元素可允许可重复
* Left Bound Index ,指的是目标值首次出现的位置,如果数组中有多个相同的元素(即重复值),左边界索引是目标值在数组中的最早出现位置
* @param nums
* @param target
* @return
*/
public int leftBound(int[] nums, int target) {
if (nums.length == 0) {
return -1;
}
if (target < nums[0] || target > nums[nums.length - 1]) {
return -1;
}
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + ((right - left) >> 2);
if (target == nums[mid]) {
right = mid - 1;
} else if (target < nums[mid]) {
right = mid - 1;
} else if (target > nums[mid]){
left = mid + 1;
}
}
// 增加边界检查
if (left >= nums.length || nums[left] != target) {
return -1;
}
return left;
}
/**
* 前置条件:有序数组,元素可允许可重复
* Right Bound Index ,指的是目标值最后出现的位置,如果数组中有多个相同的元素(即重复值),右边界索引是目标值在数组中的最晚出现位置
* @param nums
* @param target
* @return
*/
public int rightBound(int[] nums, int target) {
if (nums.length == 0) {
return -1;
}
if (target < nums[0] || target > nums[nums.length - 1]) {
return -1;
}
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + ((right - left) >> 2);
if (target == nums[mid]) {
left = mid + 1;
} else if (target > nums[mid]) {
left = mid + 1;
} else if (target < nums[mid]) {
right = mid - 1;
}
}
if (right < 0 || nums[right] != target) {
return -1;
}
return right;
}
}
package com.github.dolphinmind.array.binarusearch;
import com.github.dolphinmind.array.binarysearch.BinarySearch;
import org.junit.Test;
/**
* @author dolphinmind
* @ClassName ApiTest
* @description
* @date 2024/7/31
*/
public class BinarySearchTest {
/**
* 找房子编号,不要找房子里面的内容
*/
@Test
public void test_binarySearchRightClose() {
int[] nums = {-1, 0, 3, 5, 9, 12};
int target = 9;
BinarySearch binarySearch = new BinarySearch();
int result = binarySearch.binarySearchRightClose(nums, target);
System.out.println("左闭右闭区间选择:" + result);
}
@Test
public void test_binarySearchRightOpen() {
// int[] nums = {-1,0,3,5,9,12};
int[] nums = {};
int target = 2;
BinarySearch binarySearch = new BinarySearch();
int result = binarySearch.binarySearchRightOpen(nums, target);
System.out.println("左闭右开区间选择:" + result);
}
@Test
public void test_leftBound() {
int[] nums = {-1, 0, 3, 5, 9, 12};
int target = 9;
BinarySearch binarySearch = new BinarySearch();
int result = binarySearch.leftBound(nums, target);
System.out.println("左边界索引:" + result);
}
@Test
public void test_rightBound() {
int[] nums = {-1,0,3,5,9,12};
// int[] nums = {};
int target = 2;
BinarySearch binarySearch = new BinarySearch();
int result = binarySearch.rightBound(nums, target);
System.out.println("右边界索引:" + result);
}
}