資料結構知識整理 - 折半查詢
主要內容
基本概念
在介紹查詢演算法前我們先重溫幾個重要概念:
1)資料:客觀事物的符號表示,是所有能輸入到計算機中,並能被計算機程式處理的符號的總稱。如數學計算中用到的整數和實數,文字編輯中用到的字串,多媒體程式處理的圖形、影象、聲音及動畫等通過特殊編碼定義後的資料。
2)資料元素:資料的基本單位,在計算機中通常作為一個整體進行考慮和處理。資料元素也稱記錄,用於描述一個完整的物件,該物件可以是一名學生的資訊(記錄),某一次棋局,圖中的某一個頂點等。
3)資料項:組成資料元素的、有獨立含義的、不可分割的最小單位
4)資料結構:相互之間存在一種或多種特定關係的資料元素的集合。資料結構包括邏輯結構(邏輯表示)和儲存結構(物理實現)。
5)查詢表:由同一型別的資料元素(或記錄)構成的集合,可以由線性表、樹結構等多種資料結構來實現。
6)關鍵字:資料元素(或記錄)中的某個資料項的值,相當於“資料庫”知識中一行記錄的主碼(PRIMARY KEY)。
typedef struct /*定義資料元素結構體*/ { KeyType key; /*關鍵字*/ OtherType other; /*其他資料項*/ } Element; typedef struct /*以順序表結構定義查詢表*/ { /*順序表藉助陣列儲存資料,因此需要空間基地址base(陣列頭)和當前長度length(陣列長度)*/ Element *base; int length; } SSTable; /*SSTable表示順序查詢表*/
7)查詢:根據給定的某個值,在查詢表中確定一個其關鍵字等於給定值的資料元素(或記錄)。同樣可以類比資料庫的資料查詢。針對不同的資料結構,查詢演算法也會有所不同。
8)平均查詢長度:衡量查詢演算法效能的一個標準。
在查詢表的資料結構中,線性表是最簡單的一種。
折半查詢(Binary Search)
顧名思義,折半查詢每一次查詢比較都能使查詢範圍縮小一半,它是一種效率較高的查詢方法。
但是折半查詢要求線性表必須採用順序儲存結構,而且表中元素必須按關鍵字有序排列(有點類似資料庫的索引)。
為了標記出查詢範圍,我們要設定三個變數low、high、middle分別表示查詢範圍的下界、上界和中間位置(每次查詢後更新賦值)。
提前路過的圈毛君:“總是一不留心就把“查詢”寫成了“查詢”(資料庫留下的習慣),雖然這兩個詞意思一樣,但無奈博主是強迫症_(:з」∠)_喜歡維持前後文的用詞一致性。”
下面給出非遞迴演算法的程式碼:
int Bin_Search(SSTable t, KeyType key)
{
int low = 1, high = t.length;
while(low <= high)
{
int mid = (low + high) / 2;
if(t.base[mid] == key) return mid;
else if(t.base[mid] < key) low = mid+1;
else high = mid-1;
}
return 0;
}
在每次迴圈中,low、high、mid中的其中兩個值都要作更新,而且這個不斷縮小查詢範圍的過程也很像一個不斷深入、細化的樹或圖的遍歷過程,因此我們很容易想到一個詞——遞迴。
下面給出折半查詢的遞迴演算法的實現:
int Bin_Search(SSTable t, KeyType key, int low, int high)
{
if(low <= high)
{
int mid = (low + high) / 2;
if(t.base[mid] == key) return mid;
else if(t.base[mid] < key) low = mid + 1;
else high = mid-1;
}
Bin_Search(t, key, low, high);
}
折半查詢過程可用二叉樹來描述,由此得到的二叉樹稱為判定樹。藉助判定樹可以很快求出折半查詢的平均查詢長度。
優點:
查詢效率比順序查詢高,時間複雜度為O(log2n)。
缺點:
只適用於順序儲存結構的有序表,所以查詢前必須先對錶進行排序,而排序本身就是一種費時的運算。而且,對於有序表並不方便進行插入、刪除操作,因此折半查詢不適用於資料元素經常發生變動的查詢表。