二分查找
一、基本原理
二分查找又名折半查找,是一種效率較高的查找方法,時間複雜度是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;
}
}