二分查找
普通二分查找
描述:求解递增序列array的sta位置到end位置中间满足等于val值的位置。
适用范围:有序序列。
优势:如果数组长度为n,其算法复杂度为o(log(n)),每次查找能够将数组范围缩小到数组当前长度的一半。
求解:
1. 判断sta位置是否在end位置后面。
- sta<=end,跳转到步骤2。
- sta>end,找不到满足等于val值的位置,退出循环,返回-1,表示没有找到。
2. 计算sta位置到end的中间位置mid,mid=(sta+end)/2。计算机采用整数除法,即向下取整。跳转到步骤3。
3. 比较val与mid位置对应数值的大小。
- array[mid]>val,则满足等于val值的位置在sta和mid-1之间,将end赋值为mid-1,查找空间变为[sta,mid-1]。因为(sta+end)/2<=end,所以mid-1一定比end小,空间[sta,end]变为[sta,mid-1],空间一定缩小。跳转到步骤1,继续循环查找。
- array[mid]<val,则满足等于val值的位置在mid+1和end之间,将sta赋值为mid+1,查找空间变为[mid+1,end]。因为(sta+end)/2>=sta,所以mid+1一定比sta大,空间[sta,end]变为[mid+1,end],空间一定缩小。跳转到步骤1,继续循环查找。
- array[mid]=val,则找到满足等于val值的位置,即mid,退出循环,返回mid值。
Java源码:
public static int binSearch(int[] array,int val){
int sta=0;
int end=array,length-1;
int mid;
while(sta<=end){
//防止sta和end都在很大的时候,两者之和溢出
mid=sta+(end-sta)/2;
if(array[mid]>val)
end=mid-1;
else if(array[mid]<val)
sta=mid+1;
else
return mid;
}
return -1;
}
测试程序:
public static boolean testBinSearch(int count,int size){
boolean flag=true;
int[] array;
int length;
int rightResult, computeResult;
Random random=new Random();
int errorCount=0;
array=new int[0];
rightResult=-1;
computeResult=binSearch(array,-1);
if(rightResult!=computeResult){
PrintError(array,computeResult,rightResult,errorCount++);
flag=false;
}
while(count>0){
count--;
length=random.nextInt(size)+1;
rightResult=random.nextInt(length);
array=new int[length];
for(int i=0;i<length;i++){
if(i==0)
array[i]=random.nextInt(30);
else
array[i]=array[i-1]+random.nextInt(5);
}
computeResult=binSearch(array,array[rightResult]);
if(array[rightResult]!=array[computeResult]){
PrintError(array,computeResult,rightResult,errorCount++);
flag=false;
}
rightResult=-1;
computeResult=binSearch(array,-1);
if(rightResult!=computeResult){
PrintError(array,computeResult,rightResult,errorCount++);
flag=false;
}
}
returnflag;
}
测试报告:
执行testBinSearch(1000,100),即测试1000次,测试数组长度为1到100之间,测试结果正确。
二分查找变型1--查找第一个值等于val的位置
解析:递增序列array可以同一个值出现多次,求解该问题主要在array[mid]=val时的处理和退出循环判断条件问题。
1. 当array[mid]=val,满足第一个值等于val的值一定在sta和mid之间,在sta和mid-1之间如果没有等于val的值时,mid就是第一个位置,所以此刻范围只能缩小到sta和mid之间。
2. 退出循环判断条件问题。array[mid]=val, 则满足第一个值等于val值的位置在sta和mid之间,查找空间变为[sta,mid]。当sta=end时,mid与end相等,空间[sta,end]变为[sta,mid],空间大小没有改变,会产生死循环。所以循环条件要变为sta<end.因为循环条件变为sta<end,所以在sta=end时没有和val比较,故退出循环通过比较sta位置的值判断是否寻找满足第一个值等于val的值的位置。
Java源码:
public static int binSearchFirst(int[] array,int val){
int sta=0;
int end=array.length-1;
int mid;
while(sta<end){
mid=sta+(end-sta)/2;
if(array[mid]>val)
end=mid-1;
else if(array[mid]<val)
sta=mid+1;
else
end=mid;
}
if(array[sta]==val)
return sta;
return -1;
}
测试程序:
public static boolean testBinSearchFirst(int count,int size){
boolean flag=true;
int[] array;
int length;
int rightResult,computeResult;
Random random=new Random();
int errorCount=0;
array=new int[0];
rightResult=-1;
computeResult=binSearch(array,-1);
if(rightResult!=computeResult){
PrintError(array,computeResult,rightResult,errorCount++);
flag=false;
}
while(count>0){
count--;
length=random.nextInt(size)+1;
rightResult=random.nextInt(length);
array=new int[length];
for(int i=0;i<length;i++){
if(i==0)
array[i]=random.nextInt(30);
else if(i==rightResult)
array[i]=array[i-1]+random.nextInt(5)+1;
else
array[i]=array[i-1]+random.nextInt(5);
}
computeResult=binSearch(array,array[rightResult]);
if(array[rightResult]!=array[computeResult]){
PrintError(array,computeResult,rightResult,errorCount++);
flag=false;
}
rightResult=-1;
computeResult=binSearch(array,-1);
if(rightResult!=computeResult){
PrintError(array,computeResult,rightResult,errorCount++);
flag=false;
}
}
returnflag;
}
测试报告:
执行testBinSearchFirst(1000,100),即测试1000次,测试数组长度为1到100之间,测试结果正确。
二分查找变型2--查找最后一个值等于val的位置
解析:求解该问题主要在array[mid]与val的比较处理和退出循环判断条件问题。
1. 当array[mid]=val,满足最后一个值等于val的值一定在mid和end之间,在mid+1和end之间如果没有等于val的值时,mid就是最后一个位置,所以此刻范围只能缩小到mid和sta之间。
2. 退出循环判断条件问题。array[mid]=val, 则满足最后一个值等于val值的位置在mid和end之间,查找空间变为[mid,end]。当sta=end或者sta=end-1时,(sta+end)/2等于sta,mid与sta相等,空间[sta,end]变为[mid,end],空间大小没有改变,会产生死循环。所以循环条件要变为sta<end-1.因为循环条件变为sta<end-1,所以在sta和end-1时没有和val比较,故退出循环通过比较sta和end-1位置的值判断是否寻找满足最后一个值等于val的值的位置。
Java源码:
public static int binSearchLast(int[] array,int val){
int sta= 0;
int end= array.length - 1;
int mid;
while (sta< end - 1) {
mid = sta + (end - sta) / 2;
if(array[mid] > val)
end = mid - 1;
else if(array[mid] < val)
sta = mid + 1;
else
sta = mid;
}
if (sta== end - 1 && array[end] == val)
return end;
if (sta== end - 1 && array[sta] == val)
return sta;
if (sta== end && array[sta] == val)
return sta;
return -1;
}
测试程序:
public static boolean testBinSearchLast(int count,intsize) {
boolean flag= true;
int[] array;
int length;
int rightResult, computeResult;
Random random = new Random();
int errorCount = 0;
array = new int[0];
rightResult = -1;
computeResult = binSearchLast(array,-1);
if(rightResult != computeResult) {
PrintError(array, computeResult,rightResult, errorCount++);
flag = false;
}
while(count > 0) {
count--;
length = random.nextInt(size) + 1;
rightResult = random.nextInt(length);
array = new int[length];
for (int i =0; i < length; i++) {
if (i== rightResult) {
// 构建多个相同的值
intsameLength = random.nextInt(30) + 1;
int tmp;
if (i== 0)
tmp = random.nextInt(30);
else
tmp = array[i - 1] +random.nextInt(5) + 1;
while(sameLength > 0 && i < length)
array[i++] = tmp;
if(i<length)
array[i] = tmp + 2;
rightResult = i - 1;
} else {
if (i== 0)
array[i] =random.nextInt(30);
else
array[i] = array[i - 1] + random.nextInt(5);
}
}
computeResult = binSearchLast(array,array[rightResult]);
if(rightResult != computeResult) {
PrintError(array,computeResult, rightResult, errorCount++);
flag = false;
}
rightResult = -1;
computeResult = binSearchLast(array,-1);
if(rightResult != computeResult) {
PrintError(array,computeResult, rightResult, errorCount++);
flag = false;
}
}
return flag;
}
测试报告:
执行testBinSearchLast(1000,100),即测试1000次,测试数组长度为1到100之间,测试结果正确。
二分查找变型3--查找最接近val并且小于val的位置
解析:求解该问题主要在array[mid]与val比较处理和退出循环判断条件问题。
1. array[mid]>val,则满足最接近val并且小于val值的位置在sta和mid-1之间, 即[sta,mid-1]。继续循环查找。
2. array[mid]<val,则满足等于最接近val并且小于val值的位置在mid和end之间,查找空间变为[mid,end]。当sta=end或者sta=end-1时,(sta+end)/2等于sta,mid与sta相等,空间[sta,end]变为[mid,end],空间大小没有改变,会产生死循环。所以循环条件要变为sta<end-1.因为循环条件变为sta<end-1,所以在sta和end-1时没有和val比较,故退出循环通过比较sta和end-1位置的值判断是否寻找满足最后一个值等于val的值的位置。
3. array[mid]=val,则满足等于最接近val并且小于val值的位置在sta和mid-1之间,即[sta,mid-1]。继续循环查找。
Java源码:
public static int binSearchLess(int[] array,int val){
int sta= 0;
int end= array.length - 1;
int mid;
while (sta< end - 1) {
mid = sta + (end - sta) / 2;
if(array[mid] > val)
end = mid - 1;
else if(array[mid] < val)
sta = mid;
else
end = mid - 1;
}
if (sta== end - 1 && array[end] < val)
return end;
if (sta== end - 1 && array[sta] < val)
return sta;
if (sta== end && array[sta] < val)
return sta;
return -1;
}
测试程序:
public static boolean testBinSearchLess(int count,intsize) {
boolean flag= true;
int[] array;
int length;
int rightResult, computeResult;
Random random = new Random();
int errorCount = 0;
array = new int[0];
rightResult = -1;
computeResult = binSearchLess(array,-1);
if(rightResult != computeResult) {
PrintError(array, computeResult,rightResult, errorCount++);
flag = false;
}
while(count > 0) {
count--;
length = random.nextInt(size) + 1;
rightResult = random.nextInt(length);
array = new int[length];
for (int i =0; i < length; i++) {
if (i== rightResult) {
int tmp;
if (i== 0)
tmp = random.nextInt(30);
else
tmp = array[i - 1] +random.nextInt(5) + 1;
array[i] = tmp;
} else {
if (i== 0)
array[i] =random.nextInt(30);
else
array[i] = array[i - 1] +random.nextInt(5);
}
}
computeResult = binSearchLess(array,array[rightResult]);
rightResult--;
if(rightResult != computeResult) {
PrintError(array,computeResult, rightResult, errorCount++);
flag = false;
}
rightResult = -1;
computeResult = binSearchLess(array,-1);
if(rightResult != computeResult) {
PrintError(array,computeResult, rightResult, errorCount++);
flag = false;
}
rightResult = array.length-1;
computeResult = binSearchLess(array,Integer.MAX_VALUE);
if(rightResult != computeResult) {
PrintError(array,computeResult, rightResult, errorCount++);
flag = false;
}
}
returnflag;
}
测试报告:
执行testBinSearchLess(1000,100),即测试1000次,测试数组长度为1到100之间,测试结果正确。
二分查找变型4--查找最接近val并且大于val的位置
解析:递增序列array可以同一个值出现多次,求解该问题主要在array[mid]和val比较处理和退出循环判断条件问题。
1. array[mid]>val,则满足最接近val并且大于val值的位置在sta和mid之间, 即[sta,mid]。当sta=end时,mid与end相等,空间[sta,end]变为[sta,mid],空间大小没有改变,会产生死循环。所以循环条件要变为sta<end.因为循环条件变为sta<end,所以在sta=end时没有和val比较,故退出循环通过比较sta位置的值判断是否寻找满足第一个值等于val的值的位置。
2. array[mid]<val,则满足等于最接近val并且大于val值的位置在mid+1和end之间,即[mid+1,end]。继续循环查找。
3. array[mid]=val,则满足等于最接近val并且大于val值的位置在mid+1和end之间,即[mid+1,end]。继续循环查找。
Java源码:
public static int binSearchGreater(int[] array,int val){
int sta= 0;
int end= array.length - 1;
int mid;
while (sta< end) {
mid = sta + (end - sta) / 2;
if(array[mid] > val)
end = mid;
else if(array[mid] < val)
sta = mid + 1;
else
sta = mid + 1;
}
if (sta== end && array[sta] > val)
return sta;
return -1;
}
测试程序:
public static boolean testBinSearchGreater(int count,int size) {
boolean flag= true;
int[] array;
int length;
int rightResult, computeResult;
Random random = new Random();
int errorCount = 0;
array = new int[0];
rightResult = -1;
computeResult = binSearchGreater(array,-1);
if(rightResult != computeResult) {
PrintError(array, computeResult,rightResult, errorCount++);
flag = false;
}
while(count > 0) {
count--;
length = random.nextInt(size) + 1;
rightResult = random.nextInt(length);
array = new int[length];
for (int i =0; i < length; i++) {
if (i== rightResult) {
int tmp;
if (i== 0)
tmp = random.nextInt(30);
else
tmp = array[i - 1] +random.nextInt(5) + 1;
array[i] = tmp;
} else {
if (i== 0)
array[i] =random.nextInt(30);
else
array[i] = array[i - 1] +random.nextInt(5);
}
}
computeResult = binSearchGreater(array,rightResult==0?-1:array[rightResult-1]);
if(rightResult != computeResult) {
PrintError(array,computeResult, rightResult, errorCount++);
flag = false;
}
rightResult = 0;
computeResult = binSearchGreater(array,-1);
if(rightResult != computeResult) {
PrintError(array,computeResult, rightResult, errorCount++);
flag = false;
}
rightResult = -1;
computeResult = binSearchGreater(array,Integer.MAX_VALUE);
if(rightResult != computeResult) {
PrintError(array,computeResult, rightResult, errorCount++);
flag = false;
}
}
return flag;
}
测试报告:
执行testBinSearchGreater(1000,100),即测试1000次,测试数组长度为1到100之间,测试结果正确。
下载地址:
http://download.csdn.net/detail/ssuchange/6700749
参考资料: