二分查找
普通二分查找
描述:求解遞增序列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
參考資料: