黑馬程式設計師____折半查詢演算法
----------------------android培訓、java培訓、期待與您交流! ----------------------
折半查詢( Binary Search)是查詢有序序列的標準過程,比順序查詢有效得多,但它需要元素是有序的。它把查詢序列平均分成兩半,如劃分點元素不是我們所要查詢的元素,則在有可能包含查詢元素的子序列中查詢,重複上述查詢過程,直到找到或找遍為止。
當你查字典時,或許你使用的就是折半查詢。
演算法1折半查詢
前提條件(輸入):s={ } 是n個型別為x的有序值序列。
後置條件(輸出)當某個
時,返回索引值i,否則,返回-1
1.令ss為序列s的子序列,並置初值為s。
2.當子序列ss為空時,返回-1。
3.(不變式)當原序列s中包含x時,x必然在子序列ss中。
4. 令為子序列ss的中間元素。
5.若則返回索引i。
6. 假如,重複步驟2~7,在子序列{ }中繼續查詢與相等的元素。
7. 重複步驟2~7,在子序列{ }中查詢與x相等的元素。
注意,在演算法1的前提條件中,序列必須是事先排序的。
在java.util.Arrays中實現了折半查詢演算法。
例1折半查詢演算法
public static int binarySearch(int [] a,int x) { //前置條件:a[0]<=a[1]<=...<=a[a.length-1]; //後置條件:returns i;if i >=0,then a[i]==x; // otherwise i == -1; int lo=0,hi=a.length-1; while (lo <= hi) //步驟1 {//不變式:if a[j] == x then lo<=j<=hi; //步驟3 int i = (hi+lo)/2; //步驟4 if(a[i]==x) return i; //步驟5 else if(a[i]<x) lo = i+1; //步驟6 else hi = i-1; //步驟7 } return -1 ; //步驟2 }
定理1 折半查詢演算法是正確的。
證明:迴圈迭代開始時,子序列為輸入原序列,迴圈不變式為真。在隨後的各迴圈迭代布中;下一迭代步中的子序列是當前子序列中去掉不包含元素那半段後所剩餘的部分,因此,如果x在原序列中,則x必在下一迭代步中將要查詢的子序列在中,因此,在每一個迴圈步中,不變式總為真。
在每次迭代中,當 時,將返回索引值i。否則,子序列長度將減少百分之五十多。
因原序列為有限個元素,迴圈比在有限步內終止,因此,當演算法終止於迴圈中部(步驟5)時,將返回索引值i。或當終止於迴圈末尾(步驟6或7)時,將返回-1。從迴圈中部返回索引值i時,表明 ,否則,迴圈終止於hi<lo,也即當前子序列為空序列。在這種情況下,從迴圈不變式可知, 不在原序列中。
定理2 折半查詢演算法的執行時間為O(lg n)
證明:從定理2.3的證明過程可知,迴圈迭代次數最多為n能被2除(取整)的次數加1,也即n以2為底的對數取整加1,因此,演算法的時間複雜度為O(lg n)。
例2測試折半查詢演算法
import schaums.dswj.ArraysDemo;
public class Ex0208
{
private static final int SIZE = 16;
private static final int START= 40;
private static final int RANGE = 20;
private static int [] a = new int[SIZE];
public static void main(String [] args)
{
ArraysDemo.load(a,START,RANGE);
ArraysDemo.print(a);
test();
java.util.Arrays.sort(a);
ArraysDemo.print(a);
test();
test();
test();
}
public static void test()
{
int x = ArraysDemo.load(START,RANGE);
System.out.print("Searching for x =" +x+":\t");
int i = ArraysDemo.binarySearch(a,x);
if(i>=0) System.out.println("a["+i+"]="+a[i]);
else System.out.println("i = "+i+" --> x not found");
}
}
該程式的輸出為:
本測試程式中,test() 被呼叫4次,從程式中可以看出,test()呼叫折半查詢演算法在由load()產生的陣列中查詢x,第一次查詢x=48,儘管陣列a[8]=48,但由於陣列沒有事先排序,因此折半查詢失敗,沒有找到。在第二次呼叫test()之前,使用java.util.Arrays.sort(a)對陣列a進行排序,這樣,在a[11]找到x=55。請注意的是,此處並不是x=55的第一次出現,因為它還出現在a[10]處。由於折半查詢演算法不是順序的,當某值不只一次出現時,演算法返回的索引值是不易預見的。第三次查詢時,x=41,在a[4]處找到。第四次呼叫test()查詢x=58,沒有找到,返回值為-1。
---------------------- android培訓、java培訓、期待與您交流! ----------------------