二分查找(java)
文章目录
- 1. 基本原理
- 2. 步骤
- 3.练习
1. 基本原理
二分查找(Binary Search)是一种基于分治思想的高效搜索算法,核心逻辑是通过不断缩小搜索区间来定位目标值。其前提是数据必须为有序数组,时间复杂度为 O(log n)。
2. 步骤
1. 初始化区间:左边界 left=0,右边界 right=数组长度-1。
2. 计算中间位置 mid = left + (right - left) >> 1(防止整数溢出)。
3. 比较 arr[mid] 与目标值:
-若相等,返回 mid;
-若目标值较小,调整右边界 right = mid - 1;
-若目标值较大,调整左边界 left = mid + 1。
4. 重复步骤2-3,直到 left > right,返回未找到标志(如 -1)。
3.练习
题目1:
解题思路:
由于题目中是升序且无重复数组,可以利用二分法。因为在数组中找不到target时需要给出插入的位置,所有采用左开右闭。
class Solution {
public int searchInsert(int[] nums, int target) {
int len = nums.length;
int ans = len;
int left = 0, right = len-1;
while(left <= right) {
int mid = ((right - left) >> 1) + left;
if(nums[mid] < target) {
left = mid+1;
}else {
ans = mid;
right = mid-1;
}
}
return ans;
}
}
题目2:
解题思路:
题中可能存在三种情况:
1. target在数组范围的左边或者右边,即target比最小值小或比最大值大;
2. target在数组中存在;
3. target在数组范围内,但数组中无target。
class Solution {
public int[] searchRange(int[] nums, int target) {
int leftBorder = search(nums, target, true);
int rightBorder = search(nums, target, false);
//target在数组范围左边或右边,即左右边界无效
if(leftBorder == nums.length) {
return new int[]{-1, -1};
}
//target在数组中存在
if(rightBorder >= leftBorder) {
return new int[]{leftBorder, rightBorder};
}
//target在数组中不存在
return new int[]{-1, -1};
}
public int search(int[] nums, int target, boolean isLeft) {
int n = nums.length;
int ans = n;//n=1时,左右边界为0,1
int left = 0, right = n-1;
while(left <= right) {
int mid = ((right - left) >> 1) + left;
if(nums[mid] > target || (isLeft && nums[mid] >= target)) {
ans = mid;
right = mid-1;
}else {
left = mid+1;
}
}
return isLeft ? ans : ans-1;
}
}
题目3:
解题思路:
题目要求找平方根,可以当作在升序整数数列中找某个值,所有可以用左闭右开二分法。
class Solution {
public int mySqrt(int x) {
if(x == 0) return 0;
int ans = 0;
int left = 1, right = x;
while(left <= right) {
int mid = left + ((right - left) >> 1);
// 避免溢出
if(mid > x/mid) {
right = mid-1;
}else {
ans = mid;
left = mid+1;
}
}
return ans;
}
}
题目4:
解题思路:
要求判断有效,即严格要求存在平方根,所有要判断找出的ans是否有效。
class Solution {
public boolean isPerfectSquare(int num) {
int left = 1, right = num, ans = 0;
while(left <= right) {
int mid = left + ((right - left) >> 1);
if(mid > num/mid) {
right = mid - 1;
}else {
ans = mid;
left = mid + 1;
}
}
return (double)num / ans / ans == 1;
}
}