二分查找
一、基本原理
二分查找又名折半查找,是一种效率较高的查找方法,时间复杂度是O(logn),要求待查找序列必须有序。
二、传统代码模板
public int binarySearch(int[] nums, int target) {
int low = 0;
int high = nums.length - 1;
while (low <= high){
int mid = (low + high) / 2;
if (nums[mid] == target){
return mid;
}else if (nums[mid] < target){
low = mid + 1;
}else {
high = mid - 1;
}
}
return low;
}
存在的问题如下:
1、当待查找序列的长度非常大的时候,取mid时可能出现整型溢出。
2、退出while循环后,需要考虑到底是返回low还是high。
三、改进版
1、中位数取法
int mid = low + (high - low) / 2;
或者
int mid = (low + high) >>> 1;
2、while循环条件修改为low<high,当退出循环的时候,low==high成立,此时返回值就可以任选其一。
public int binarySearch(int[] nums, int target) {
int low = 0;
int high = nums.length;
int mid = 0;
while (low < high){
mid = low + (high - low) / 2;
if (judge()){
low = mid + 1;
}else {
high = mid;
}
}
return low;
}
或
public int searchInsert(int[] nums, int target) {
int low = 0;
int high = nums.length;
int mid = 0;
while (low < high){
mid = low + (high - low + 1) / 2;
if (judge()){
high = mid - 1;
}else {
low = mid;
}
}
return low;
}
四、例题
class Solution {
public int searchInsert(int[] nums, int target) {
int low = 0;
int high = nums.length;
int mid = 0;
while (low < high){
mid = low + (high - low) / 2;
//当nums[mid]<target时直接更新下边界,因为此时的mid一定不是插入位置
//但是当nums[mid]>target时,却不能直接更新上边界high=mid-1,因为mid可能为插入位置
if (nums[mid] < target){
low = mid + 1;
}else {
high = mid;
}
}
return low;
}
}
class Solution {
public int mySqrt(int x) {
if (x == 0 || x == 1){
return x;
}
int low = 0;
int high = x / 2;
while (low < high){
int mid = low + (high - low + 1) / 2;
//根据题意,最后结果是向下取整,所以当mid>x/mid时,直接更新上边界,因为mid的平方不可能比x大
//但是当mid<x/mid,不能直接更新下边界low=mid+1,因为有可能mid就是最后要求的值。
if (mid > x / mid){
high = mid - 1;
}else {
low = mid;
}
}
return low;
}
}
class Solution {
public int findPeakElement(int[] nums) {
if (nums.length == 1){
return 0;
}
if (nums.length == 2){
if (nums[0] > nums[1]){
return 0;
}else {
return 1;
}
}
int start = -1;
int end = nums.length;
while (start < end){
int mid = start + (end - start) / 2;
int left = mid - 1 < 0 ? Integer.MIN_VALUE : nums[mid - 1];
int right = mid + 1 == nums.length ? Integer.MIN_VALUE : nums[mid + 1];
//如果nums[mid]的值大于左右值,那么mid即为峰值位置
//如果nums[mid]的值比左边大,比右边小,说明nums[mid]处于一个上升的序列当中,那么需要更新下边界,继续寻找峰值,但是不能直接start=mid+1,因为此时只能确定[start,mid-1]区间是要舍弃的区间,需要去[mid,end]区间中寻找峰值。更新上边界的原理与此相似。
if (nums[mid] > left && nums[mid] > right){
return mid;
}else if (nums[mid] > left && nums[mid] < right){
start = mid;
}else {
end = mid;
}
}
return 0;
}
}
1838

被折叠的 条评论
为什么被折叠?



