二分算法主要分为整数二分和浮点数二分。 在处理有关于二分的问题时,所给数组是否满足单调性,只是我们选择使用二分算法的一个充要条件。在解答关于二分的问题时,一个非常重要的步骤便是关于分界点的判断。以下笔者对着两个算法做出详细的分析
一.整数二分
1.算法的基本思想
我们给以将所给区间划分成两个部分,一部分满足某种性质,另一部分不满足该性质。如何找出这个分界点?这个时候便要用到二分的思想。
(1).mid值什么时候为(L+R+1)/2什么时候是(L+R)/2?
在写二分查找算法时,我们首先写check函数,当发现我们需要将L变为mid时,即L = mid,这时我们就返回关于mid的代码将其设置为mid = (L+R+1)/2。反之,当我们发现需要将R变为mid时,即R=mid,这时我们需要将mid设置为mid = (L+R)/2.
(2)为什么mid=(L+R+1)/2 ?
我们假设 mid = (L+R)/2,当R=L-1时,我们会发现mid = L,令L=mid =L,这样会陷入一个死循环。
2…算法模板
/*当区间[l,r]被划分成[l,mid]和[mid+1,r]时使用*/
int b_search(int l,int r){
while(l<r){
int mid = (l+r+1)/2;
if(checked(mid)){
l = mid;
} else {
r = mid-1;
}
}
}
/*当区间[l,r]被划分成[l,mid-1]和[mid,r]时使用*/
int b_search(int l,int r){
while(l<r){
int mid = (l+r)/2;
if(checked(mid)){
r = mid;
} else {
l = mid+1;
}
}
}
3.例题
数的范围
给定一个按照升序排列的长度为 n 的整数数组,以及 q个查询。对于每个查询,返回一个元素 k的起始位置和终止位置(位置从 0开始计数)。如果数组中不存在该元素,则返回 -1 -1。
输入格式
第一行包含整数 n 和 q,表示数组长度和询问个数。
第二行包含 n 个整数(均在 1∼10000范围内),表示完整数组。
接下来 q 行,每行包含一个整数 k,表示一个询问元素。
输出格式
共 q 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回 -1 -1。
输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1
解题思想:
注意题干中所给数组的条件为"升序排列",那么必然可以使用二分查找算法。关键点是check函数的判断条件。
起始点的判断条件:mid值>=query值
终止点的判断条件:mid值<=query值
没有查询到:当while循环截止时,lcopy=rcopy,只需要判断data[lcopy]是否与query值相等即可。
代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int q = sc.nextInt();
int[] data = new int[n];
int[] query = new int[q];
for(int i=0;i<n;i++) {
data[i] = sc.nextInt();
}
for(int i=0;i<q;i++) {
query[i] = sc.nextInt();
}
sc.close();
for(int i=0;i<q;i++) {
b_searchStart(0,n-1,query[i],data);
}
}
//寻找起始坐标
public static void b_searchStart(int l,int r,int k,int[] data) {
int lcopy = l,rcopy = r;
while(lcopy<rcopy) {
int mid = lcopy+rcopy >> 1;
if(data[mid] >= k) {
rcopy = mid;
} else {
lcopy = mid +1;
}
}
if(data[lcopy] != k) {
System.out.println("-1 -1");
} else {
System.out.print(lcopy+" ");
b_searchEnd(l,r,k,data);
}
}
//寻找结束坐标
public static void b_searchEnd(int l,int r,int k,int[] data) {
int lcopy = l,rcopy = r;
while(lcopy<rcopy) {
int mid = (lcopy+rcopy+1) >> 1;
if(data[mid]<=k) {
lcopy = mid;
} else {
rcopy = mid -1;
}
}
System.out.println(rcopy);
}
}