二分查找相关算法
Posted keep求索
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二分查找相关算法相关的知识,希望对你有一定的参考价值。
上一节我讲了二分查找的原理,并且介绍了最简单的一种二分查找的代码实现:
//简单二分查找
public static int bsearchSimple(int[] a, int n, int value){
int low = 0;
int high = n-1;
while (low <= high){
int mid = low + ((high - low) >> 1);
if(a[mid] == value){
return mid;
} else if(a[mid] < value) {
low = mid + 1;
} else {
high = mid -1;
}
}
return -1;
}
今天我们来讲几种二分查找的变形问题。
不知道你有没有听过这样一个说法:“十个二分九个错”。二分查找虽然原理极其简单,但是想要写出没有 Bug 的二分查找并不容易。
唐纳德·克努特(Donald E.Knuth)在《计算机程序设计艺术》的第 3 卷《排序和查找》中说到:“尽管第一个二分查找算法于 1946 年出现,然而第一个完全正确的二分查找算法实现直到 1962 年才出现。”
你可能会说,我们上一节学的二分查找的代码实现并不难写啊。那是因为上一节讲的只是二分查找中最简单的一种情况,在不存在重复元素的有序数组中,查找值等于给定值的元素。最简单的二分查找写起来确实不难,但是,二分查找的变形问题就没那么好写了。
二分查找的变形问题很多,主要有以下几个。
今天的内容,我都以数据是从小到大排列为前提,如果你要处理的数据是从大到小排列的,解决思路也是一样的。
变体一:查找第一个值等于给定值的元素
//查找第一个值等于给定值的数字
public static int bsearchFistEqual(int[] a, int n , int value){
int low = 0;
int high = n-1;
while (low <= high){
int mid = low + ((high - low) >> 1);
if(a[mid] < value){
low = mid +1;
} else if (a[mid] > value){
high = mid -1;
} else {
if(mid == 0 || a[mid -1] != value){
return mid;
} else {
high = mid -1;
}
}
}
return -1;
}
变体二:查找最后一个值等于给定值的元素
//查找最后一个值等于给定值的数字
public static int bsearchLastEqual(int[] a, int n , int value){
int low = 0;
int high = n-1;
while (low <= high){
int mid = low + ((high - low) >> 1);
if(a[mid] < value){
low = mid +1;
} else if (a[mid] > value){
high = mid -1;
} else {
if(mid == n-1 || a[mid + 1] != value){
return mid;
} else {
low = mid + 1;
}
}
}
return -1;
}
变体三:查找第一个大于等于给定值的元素
//查找第一个大于等于给定值的数字
public static int bsearchFistBigger(int[] a, int n , int value){
int low = 0;
int high = n-1;
while (low <= high){
int mid = low + ((high - low) >> 1);
if(a[mid] < value){
low = mid +1;
} else if (a[mid] >= value){
if(mid == 0 || a[mid - 1] < value){
return mid;
}
high = mid -1;
}
}
return -1;
}
变体四:查找最后一个小于等于给定值的元素
//查找最后一个小于等于给定值的数字
public static int bsearchLastSmaller(int[] a, int n , int value){
int low = 0;
int high = n-1;
while (low <= high){
int mid = low + ((high - low) >> 1);
if(a[mid] <= value){
if(mid == n -1 || a[mid + 1] > value){
return mid;
}
low = mid +1;
} else if (a[mid] > value){
high = mid -1;
}
}
return -1;
}
二分查找更适合用在“近似”查找问题,在这类问题上,二分查找的优势更加明显。比如今天讲的这几种变体问题,用其他数据结构,比如散列表、二叉树,就比较难实现了。
变体的二分查找算法写起来非常烧脑,很容易因为细节处理不好而产生 Bug,这些容易出错的细节有:终止条件、区间上下界更新方法、返回值选择。
以上是关于二分查找相关算法的主要内容,如果未能解决你的问题,请参考以下文章