數據結構:靜態查找表(C語言版)
1.寫在前面
►從查找說起:
在英漢字典中查找某個英文單詞的中文解釋;在新華字典中查找某個漢字的讀音、含義;在對數表、平方根表中查找某個數的對數、平方根;郵遞員送信件要按收件人的地址確定位置等等。
從計算機、計算機網絡中查找特定的信息,就需要在計算機中存儲包含該特定信息的表。查找是許多程序中最消耗時間的一部分。因而,一個好的查找方法會大大提高運行速度。
►先討論靜態查找表:
靜態查找表應該是查找中最為簡單的。僅僅是在固定的表中對元素的查找,而不涉及修改表中的元素。
我們討論的是 在無序表、順序表中的遍歷查找和快速的折半查找。
2.代碼分解
►無序表上的順序查找
方式:從查找表的一端依序與表中的元素進行比較。
代碼是很簡單的,直接給出,以便后續分析:
#include <iostream> typedef int KeyType; typedef struct { KeyType key; int info; }ElemType; typedef struct { ElemType *elem; // 數據元素存儲空間基址,建表時 int length; // 表的長度 } SSTable; int Sq_search(SSTable ST, KeyType key) { // 在無序表中查找元素key,查找成功時,返回元素在表中的位置 ,否則返回0 int i=ST.length; while (i>=1&&ST.elem[i].key!=key) i--; return i; } int Init_search(SSTable &ST,int length)//初始化表 { ST.length=length; ST.elem = (ElemType *)malloc(sizeof(ElemType)*(length+1)); } int Creat_search(SSTable &ST,int length)//創建表 { ElemType *ptr = ST.elem; int temp =0; int i=0; ptr++; //我們將第一個元素空出來! while (temp!=-1&&(i++)<length) { scanf("%d",&temp); ptr++->key=temp; } } int main() { SSTable table; Init_search(table,5); Creat_search(table,5); printf("已經找到位置:%d",Sq_search(table, 13)); return 0; }
說明:
請注意,我們在0號位置留空,在這里僅僅是為了直觀顯示索引位置!
►設置監視哨
但是我們還可以優化這段代碼,那就是設置監視哨。
所謂監視哨,就是將空出來的下標為0的這個元素的值設為Key.
分析:
這樣我們就不用多次判斷i是否越界,因為就算靜態表中找不到,也會在0位置上配對成功,返回0!
while (i>=1&&ST.elem[i].key!=key) i--; 改為: ST.elem[0].key = key; //監視哨:下標為0的位置存放待查找的元素 while (ST.elem[i].key!=key)
我們來分析一下算法復雜度:
►有序表上的順序查找
方式:在有序表上查找的時候,我們可以對無序查找進行優化:
int i = ST.length; ST.elem[0].key = key; while (key < ST.elem[i].key) i--; if (key == ST.elem[i].key) return i; return 0
即當ST.elem[i].key<= ST.elem[0].key 的時候我們就停止查找!
為什么呢?因為ST.elem[i].key<= ST.elem[0].key的時候是兩種可能,要么小於,既然<就無須再比了,要是=也就得出結果了!
►有序表上的折半查找
方法:
我們把要查找的值x與數組的中間值mid進行比較,如果說x<mid,那么因為數組有序,mid右邊的數字都會大於x,所以去mid右邊查找是徒勞的。此時我們僅僅需要去mid左邊查找即可!同理我們把mid左邊也看成是一個數組,我們就可以重復上述操作了!
總之,先確定待查記錄所在的范圍(區間),若待查記錄等於表中間位置上的記錄,則查找成功;否則,縮小查找范圍,即若待查記錄小於中間位置上的元素,則下一次到前半區間進行查找,若待查記錄大於中間位置上的元素,則下一次到后半區間進行查找。
核心代碼如下:
while (low<=high) { int mid= low + [(high-low)/2]; if (ST.elem[mid].key==key) return mid; //查找成功 else if (key < ST.elem[mid].key) high=mid-1; //下一次到前半區間查找 else low=mid+1; //下一次到后半區間查找 }
我們分析一下這個結束條件: while(low<=high)
當low=high的時候,我們已經可以確定這是能夠進行比較的最后一個元素了,因為此時不可能再對數組進行左右划分!
如果此時,mid元素不等於Key的話,可以判定查找失敗!
若此時mid<key,low+1就會大於hi
若此時mid>key,hi-1就會小與low所以自然就會結束循環!