1. 程式人生 > >查詢演算法:二分查詢、順序查詢

查詢演算法:二分查詢、順序查詢

查詢演算法

查詢演算法是在存在的序列(list) 中查詢特定的目標(target),要求序列中每個記錄必須與一個關鍵詞(key)關聯才能進行查詢。
查詢演算法通常需要兩個輸入: 1、被查詢的序列 2、要查詢的關鍵詞 查詢演算法的輸出引數和返回值: 1、返回型別為 Error_code 的值用以表示是否查詢成功 2、如果查詢成功,返回 success, 輸出引數 position 定位到目標所在位置 3、如果查詢失敗,返回 not present,輸出引數可能是未定義或不同於已有位置的任何值

順序查詢演算法

順序查詢演算法的思路很簡單:從表的第一個元素開始一個一個向下查詢,如果有和目標一致的元素,查詢成功;如果到最後一個元素仍沒有目標元素,則查詢失敗。

【實驗說明】

題目:編寫一個程式,對順序表{3,6,2,10,1,8,5,7,4,9},採用順序查詢關鍵字5的過程。要求輸出:
1)原順序表;2)查詢到關鍵字的位置;3)進行比較的次數。
1.首先要編寫類表List。需要滿足最基本的操作插入insert(),獲取retrieve(),以及得到大小size()。
2.我們觀察題目要求,表中雖然儲存的是簡單的整數,但如果我們使用List<int>物件顯然無法記錄比較次數,所以我們自己寫一個類Key通過其內部int型別的資料成員來記錄存於表中的值,並模仿int基本的邏輯操作,即編寫過載邏輯運算子,同時增加一個靜態資料成員comparisons用於記錄其比較操作的次數。
3.準備工作做好之後開始編寫順序查詢演算法。演算法的思路很簡單,也較易實現,從表中第一個元素開始比較,發現目標則返回元素所在表中位置;若遍歷之後沒有目標,則查詢失敗,返回-1表示表中沒有目標元素。 4.按題目要求編寫最後的輸出函式。

【相關程式碼】

函式 sequential_search
  1. int sequential_search(const List<int> &the_list,  
  2.                       const Key &target)  
  3. /*Post: If an entry in the_list is equal to target, then return the position 
  4.         of this entry.  
  5.         Otherwise return -1  
  6. */
  7. {  
  8.     int position;  
  9.     int
     s=the_list.size();  
  10.     for(position=0;position<s;position++){  
  11.         int data;  
  12.         the_list.retrieve(position,data);  
  13.         if(data==target){  
  14.             return position;  
  15.         }  
  16.     }  
  17.     return -1;  
  18. }  

二分查詢演算法

二分查詢前提是表是按遞增或遞減順序的規範表。此次實驗中我們使用的是遞增表。
二分查詢從表中間開始查詢目標元素。如果找到一致元素,則查詢成功。如果中間元素比目標元素小,則仍用二分查詢方法查詢表的後半部分(表是遞增排列的),反之中間元素比目標元素大,則查詢表的前半部分。

【實驗說明】

題目:編寫一個程式,對有序表{1,2,3,4,5,6,7,8,9,10},採用二分查詢關鍵字9的過程。要求輸出:
1)原順序表;2)查詢到關鍵字的位置;3)進行比較的次數。
1.二分查詢演算法的前提是表必須是有序的,如題目中是遞增方式排列的表。實現表的有序一方面是使用者規範輸入,另一方面我們也可以編寫有序的類來方便使用者的輸入。
所以從List中派生類Oredered_list,重新編寫函式Error_code insert(int position,const Record &data),使插入的位置不滿足表的有序條件時,不能插入。
同時編寫插入的過載函式 Error_code insert(const Record &data),可以直接插入到合適的位置,方便使用者輸入。
2.仍使用題目1中的Key來表示目標
3.實現二分查詢演算法。通過書中的學習,我們直接使用新增相等判斷的二分查詢演算法。即每次從表的中間元素開始比較,如果得到目標則返回元素所在表中位置;如果中間元素小於目標元素,則對右半部分繼續二分查詢;反之對前半部分表進行二分查詢。若最後都沒有目標元素,返回-1用以表示表中沒有目標元素。
4.仍使用題目1編寫的輸出函式將結果輸出。
/*注意這裡因為Ordered_list是從List中派生而來,所以雖然print_out函式中第一個引數型別是List<int>,仍可以使用,而不用編寫過載函式*/

【相關程式碼】

函式 binary_search
  1. int binary_search(const Ordered_list<int> &the_list,  
  2.                     const Key &target)  
  3. /*Post: If an entry in the_list is equal to target, then return the position 
  4.         of this entry.  
  5.         Otherwise return -1  
  6. */
  7. {  
  8.     int position;  
  9.     int data;  
  10.     int bottom=0,top=the_list.size()-1;  
  11.     while(bottom<=top){  
  12.         position=(bottom+top)>>1;   
  13.         the_list.retrieve(position,data);  
  14.         if(data==target)  
  15.             return position;  
  16.         if(data<target)bottom=position+1;  
  17.         else top=position-1;  
  18.     }  
  19.     return -1;  
  20. }  

【過程記錄】

實驗截圖:

【結果分析】

A.實現順序查詢演算法

1.順序查詢演算法思路很簡單,就是一種遍歷的思想,一個個查詢目標元素,實現也很簡單。
2.對於有n個元素的表適用順序查詢。比較次數:不成功:比較n次。成功查詢:最好的情況為1次,即第一個元素即為目標元素;最差的情況為n次;平均比較次數(n+1)/2次。
所以當表很大時,順序查詢的代價是很大的。
3.順序查詢演算法不會有重複的比較出現,即一旦找到即成功,但同時這種代價是當表中有重複的目標元素時(比如有多個目標元素)我們只能得到第一個元素的位置。

B.實現二分查詢演算法

1.二分查詢法思路:遞增排列的表,首先從中間元素開始查詢,如果元素比目標元素小,則查詢後半部分表,反之查詢前半部分表,並重復這一過程。這樣每次查詢中我們都把表的長度減半。
2.二分查詢在實現中有量bottom和top,每次減半的過程體現在bottom和top的改變上,在程式碼的實現上可以使用單純的迴圈或者用函式遞迴的思想。
遞迴思想更容易理解,但編寫之後我們發現函式是尾遞迴,尾遞迴通常可以用簡單的迴圈實現,迴圈在操作來說沒有了函式呼叫的過程,更節省時間和空間。
3.編碼中小小地方的改動可能對程式有很大的改觀。
如上述兩種二分查詢binary_search_1(不比較等於的情況)binary_search_2(新增等於情況)