數據結構50:二分查找法(折半查找法)


折半查找,也稱二分查找,在某些情況下相比於順序查找,使用折半查找算法的效率更高。但是該算法的使用的前提是靜態查找表中的數據必須是有序的。

例如,在 {5,21,13,19,37,75,56,64,88 ,80,92}這個查找表使用折半查找算法查找數據之前,需要首先對該表中的數據按照所查的關鍵字進行排序: {5,13,19,21,37,56,64,75,80,88,92}

在折半查找之前對查找表按照所查的關鍵字進行排序的意思是:若查找表中存儲的數據元素含有多個關鍵字時,使用哪種關鍵字做折半查找,就需要提前以該關鍵字對所有數據進行排序。

折半查找算法

對靜態查找表 {5,13,19,21,37,56,64,75,80,88,92}采用折半查找算法查找關鍵字為 21 的過程為:


圖 1 折半查找的過程(a)

如上圖 1 所示,指針 low 和 high 分別指向查找表的第一個關鍵字和最后一個關鍵字,指針 mid 指向處於 low 和 high 指針中間位置的關鍵字。在查找的過程中每次都同 mid 指向的關鍵字進行比較,由於整個表中的數據是有序的,因此在比較之后就可以知道要查找的關鍵字的大致位置。

例如在查找關鍵字 21 時,首先同 56 作比較,由於 21 < 56,而且這個查找表是按照升序進行排序的,所以可以判定如果靜態查找表中有 21 這個關鍵字,就一定存在於 low 和 mid 指向的區域中間。

因此,再次遍歷時需要更新 high 指針和 mid 指針的位置,令 high 指針移動到 mid 指針的左側一個位置上,同時令 mid 重新指向 low 指針和 high 指針的中間位置。如圖 2 所示:


圖 2 折半查找的過程(b)
 
同樣,用 21 同 mid 指針指向的 19 作比較, 19 < 21,所以可以判定 21 如果存在,肯定處於 mid 和 high 指向的區域中。所以令 low 指向 mid 右側一個位置上,同時更新 mid 的位置。



圖 3 折半查找的過程(3)
當第三次做判斷時,發現 mid 就是關鍵字 21 ,查找結束。

注意:在做查找的過程中,如果 low 指針和 high 指針的中間位置在計算時位於兩個關鍵字中間,即求得 mid 的位置不是整數,需要統一做取整操作。

折半查找的實現代碼:
#include <stdio.h>
#include <stdlib.h>
#define keyType int
typedef struct
{ keyType key;  
// 查找表中每個數據元素的值 // 如果需要,還可以添加其他屬性 }ElemType; typedef struct
{ ElemType *elem;  // 存放查找表中數據元素的數組 int length;     // 記錄查找表中數據的總數量 }SSTable;
// 創建查找表 void Create(SSTable **st, int length)
{ (
*st) = (SSTable*)malloc(sizeof(SSTable)); (*st)->length = length; printf("輸入表中的數據元素:\n"); // 根據查找表中數據元素的總長度,在存儲時,從數組下標為 1 的空間開始存儲數據 for (int i=1; i<=length; i++)
   { scanf(
"%d", &((*st)->elem[i].key)); } }
//折半查找算法 int Search_Bin(SSTable *ST, keyType key)
{
int low = 1;  //初始狀態 low 指針指向第一個關鍵字 int high = ST->length;  //high 指向最后一個關鍵字 int mid; while (low <= high)
  { mid
= (low+high) / 2;  // int 本身為整形,所以,mid 每次為取整的整數 if (ST->elem[mid].key == key)  // 如果 mid 指向的同要查找的相等,返回 mid 所指向的位置 { return mid; }
     else if(ST->elem[mid].key > key)  // 如果mid指向的關鍵字較大,則更新 high 指針的位置 { high = mid-1; } // 反之,則更新 low 指針的位置 else
   { low = mid + 1; } }
return 0; } int main(int argc, const char * argv[])
{ SSTable
*st; Create(&st, 11); getchar(); printf("請輸入查找數據的關鍵字:\n"); int key; scanf("%d", &key); int location = Search_Bin(st, key); //如果返回值為 0,則證明查找表中未查到 key 值, if (location == 0)
   { printf(
"查找表中無該元素"); }
   else
   { printf("數據在查找表中的位置為:%d", location); } return 0; }
以圖
1 的查找表為例,運行結果為: 輸入表中的數據元素: 5 13 19 21 37 56 64 75 80 88 92 請輸入查找數據的關鍵字: 21 數據在查找表中的位置為:4

 

折半查找的性能分析

折半查找的運行過程可以用二叉樹來描述,這棵樹通常稱為“判定樹”。例如圖 1 中的靜態查找表中做折半查找的過程,對應的判定樹如圖 4:

圖 4 折半查找對應的判定樹

在判定樹中可以看到,如果想在查找表中查找 21 的位置,只需要進行 3 次比較,依次和 56、19、21 進行比較,而比較的次數恰好是該關鍵字所在判定樹中的層次(關鍵字 21 在判定樹中的第 3 層)。

對於具有 n 個結點(查找表中含有 n 個關鍵字)的判定樹,它的層次數至多為: log2n + 1(如果結果不是整數,則做取整操作,例如:  log211 +1 = 3 + 1 = 4 )。

同時,在查找表中各個關鍵字被查找概率相同的情況下,折半查找的平均查找長度為: ASL = log2(n+1) – 1

總結

通過比較折半查找的平均查找長度,同前面介紹的順序查找相對比,明顯折半查找的效率要高。但是折半查找算法只適用於有序表,同時僅限於查找表用順序存儲結構表示。
當查找表使用鏈式存儲結構表示時,折半查找算法無法有效地進行比較操作(排序和查找操作的實現都異常繁瑣)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM