二分查找模板
快速了解一下二分查找
二分法,又称折半查找,时间复杂度O(logn)的查找算法,前提是有序数列,最基本的应用就是“猜数字”(一次缩小一半的范围,直到猜中数字),左闭右闭区间[l,r],它的拓展也与“猜数字”相似,变成了搜索大于等于target的第一个数或小于等于target的最后一个数(也就是最接近的数)
查找已存在的数
public static int findOne(int[] array,int target){
int l = 0;
int r = array.length - 1;
while(l <= r){
int mid = l + (r - l) / 2;//防止l+r超出int的存储大小,下同
if(array[mid] < target) l = mid + 1;
else if(array[mid] > target) r = mid - 1;
else return mid;
}
return -1;
}
查找大于等于target的第一个数
public static int findFirstGreater(int[] array,int target){
if(array.length == 0) return -1;
int l = 0;
int r = array.length - 1;
while(l < r){
int mid = l + (r - l) / 2;
if(array[mid] < target) l = mid + 1;
else r = mid;//找到符合的数不要立即返回,继续缩小区间,right = mid
}
if(l==array.length-1 && array[l]<target){
return -1;// 所有的数都比target小,这时找不到答案
}
// 所有的数都比target大的情况l不会被mid+1赋值,即初始值0
return l >= array.length ? -1 : l;
}
查找小于等于target的最后一个数
public static int findLastLess(int[] array,int target){
if(array.length == 0) return -1;
int l = 0;
int r = array.length - 1;
while(l < r){
int mid = l + (r - l) / 2;
if(array[mid] <= target) l = mid + 1;
else r = mid;
}
if(r==array.length-1 && array[r]<=target){
return r;// 所有的数都小于或者等于target,这个时候找到的是最后一个数,不用减一
}
// r-1处理了所有的数都比target大的情况
return r - 1;//此时的r为大于target的第一个数
}
验证
- 生成随机数据
// size是生成的列表大小,numBound是元素范围大小
public static int[] getSortedRandomList(int size,int numBound){
int[] nums = new int[size];
Random random = new Random();
for(int i=0;i<size;i++){
nums[i] = random.nextInt(numBound);
}
Arrays.sort(nums);
return nums;
}
- 结果生成函数
// condition等于0返回eq,大于0返回firstGreater,小于0返回lastLess
public static int check(int[] list,int target,int condition){
int firstGreater=-1;
int lastLess=-1;
int eq = -1;
for (int num : list) {
if (num <= target) {
lastLess = num;
}
if(num == target){
eq = num;
}
if (num >= target) {
firstGreater = num;
break;
}
}
return condition==0?eq:(condition>0?firstGreater:lastLess);
}
- 完整代码
import java.util.*;
public class Main {
public static int[] getSortedRandomList(int size,int numBound){
int[] nums = new int[size];
Random random = new Random();
for(int i=0;i<size;i++){
nums[i] = random.nextInt(numBound);
}
Arrays.sort(nums);
return nums;
}
public static int check(int[] list,int target,int condition){
int firstGreater=-1;
int lastLess=-1;
int eq = -1;
for (int num : list) {
if (num <= target) {
lastLess = num;
}
if(num == target){
eq = num;
}
if (num >= target) {
firstGreater = num;
break;
}
}
return condition==0?eq:(condition>0?firstGreater:lastLess);
}
public static int findFirstGreater(int[] array,int target){
if(array.length == 0) return -1;
int l = 0;
int r = array.length - 1;
while(l < r){
int mid = l + (r - l) / 2;
if(array[mid] < target) l = mid + 1;
else r = mid;//找到符合的数不要立即返回,继续缩小区间,right = mid
}
if(l==array.length-1 && array[l]<target){
return -1;// 所有的数都比target小,这时找不到答案
}
// 所有的数都比target大的情况l不会被mid+1赋值,即初始值0
return l >= array.length ? -1 : l;
}
public static int findLastLess(int[] array,int target){
if(array.length == 0) return -1;
int l = 0;
int r = array.length - 1;
while(l < r){
int mid = l + (r - l) / 2;
if(array[mid] <= target) l = mid + 1;
else r = mid;
}
if(r==array.length-1 && array[r]<=target){
return r;// 所有的数都小于或者等于target,这个时候找到的是最后一个数,不用减一
}
// r-1处理了所有的数都比target大的情况
return r - 1;//此时的r为大于target的第一个数
}
public static void main(String[] args) {
Random random = new Random();
for(int i=0;i<100;i++){
int flag=0;
int target = random.nextInt(1000);
int[] list = getSortedRandomList(100,1000);
int greaterIndex = findFirstGreater(list,target);
int lessIndex = findLastLess(list,target);
int greater = greaterIndex==-1?-1:list[greaterIndex];
int less = lessIndex==-1?-1:list[lessIndex];
int greaterAns = check(list,target,1);
int lessAns = check(list,target,-1);
if(greater!=greaterAns){
flag=1;
System.out.println("---------------greater error--------------");
System.out.println(Arrays.toString(list));
System.out.println("target="+target);
System.out.println("greater="+greater+" greaterAns="+greaterAns);
System.out.println("------------------------------------------");
}
if(less!=lessAns){
flag=1;
System.out.println("----------------less error----------------");
System.out.println(Arrays.toString(list));
System.out.println("target="+target);
System.out.println("less="+less+" lessAns="+lessAns);
System.out.println("------------------------------------------");
}
if(flag==0){
System.out.println("[success] target="+target+" greater="+greater+" less="+less);
}
}
}
}
关于测试
以上的二分查找模板经过我自己测试,可以满足99%的情况(其实我觉得差不多够用了,懒得用数学证明到底是不是100%)
我把我自己编写的测试函数写在上面了,有需要可以自己测一测,下面是我测试的一些情况。
- 奇/偶数个随机数值(1-1000)的元素
- 空列表
- 列表中存在/不存在target
- 元素全部大于target
- 元素全部小于target
- 元素全部大于或者等于target
- 元素全部小于或者等于target