23.資料結構 查詢

後現代的打工人發表於2020-12-08

1.查詢的基本概念

查詢表是由同一型別的資料元素(或記錄)構成的集合。由於“集合”中的資料元素之間存在著鬆散的關係,因此查詢表是一種應用靈便的結構。

關鍵字:用來標識一個資料元素(或記錄)的某個資料項的值。

主關鍵字:可唯一地標識一個記錄的關鍵字。(一對一,類似學號,工號)

次關鍵字:反之,用以標識若干記錄的關鍵字。(一對多,類似姓名,成績)

若查詢表中存在這樣一個記錄,則稱“查詢成功”

查詢結果給出整個記錄的資訊,或指示該記錄在查詢表中的位置

否則稱“查詢不成功”

查詢結果給出“空記錄”“空指標”

對查詢表經常進行的操作

1.查詢某個“特定”的資料元素是否在查詢表中;

2.檢索某個“特定”的資料元素的各種屬性;

3.在查詢表中插入一個資料元素;

4.刪除查詢表中的摸一個資料元素。

查詢表可分為兩類:

靜態查詢表:

僅作“查詢”(檢索)操作的查詢表。

動態查詢表:

“插入”“刪除”操作的查詢表。

有時在查詢之後,還需要將“查詢”結果為“不在查詢表中”的資料元素插入到查詢表中;或者,從查詢表中刪除其“查詢”結果為“在查詢表中”的資料元素,此類表為動態查詢表。

查詢演算法的評價指標:關鍵字的平均比較次數,也稱平均查詢長度ASL

(關鍵字比較次數的期望值)

n:記錄的個數

pi:查詢第i個記錄的概率(通常認為pi=1/n)

ci:找到第i個記錄所需的比較次數


2.線性表的查詢

2.1順序查詢(線性查詢)

應用範圍:

  順序表或線性連結串列表示的靜態查詢表

  表內元素之間無序

順序表的表示:

資料元素型別定義:

typedef struct
{
	KeyType key;//關鍵字域
	...//其他域
}ElemType;

typedef struct
{
	//順序表結構型別定義
	ElemType* R;//表基址
	int length;//表長
}SSTable;
SSTable ST;//定義順序表ST

 在順序表ST中查詢值為key的資料元素(從最後一個元素開始比較)

int Search_Seq(SSTable ST, KeyType key)
{
	//若成功返回其位置資訊,否則返回0
	for (i = ST.length; i >= 1; i--)
	{
		if (ST.R[i].key == key)
		{
			return i;
		}
	}
	return 0;
}

//其他形式
int Search_Seq(SSTable ST, KeyType key)
{
	for (i = ST.length; ST.R[i].key != key; i--)
		if (i <= 0)
			break;
	if (i > 0)
		return i;
	else
		return 0;
}

int Search_Seq(SSTable ST, KeyType key)
{
	for (i = ST.length; ST.R[i].key != key && i>0; i--);
	if (i > 0)
		return i;
	else
		return 0;
}

順序查詢 改進:把待查關鍵字key存入表頭(“哨兵”、“監視哨”),從後往前逐個比較,可免去查詢過程中每一步都要檢測釋放查詢完畢,加快速度。

int Search_Seq(SSTable ST, KeyType key)
{
	//設定監視哨的順序查詢
	ST.R[0].key = key;
	for (i = ST.length; ST.R[i].key != key; i--);
	return i;
}

當ST.length較大時,此改進能使進行一次查詢所需的平均時間幾乎減少一半。

時間效率分析:

比較次數與key位置有關:

查詢第i個元素,需比較n-i+1次;

查詢失敗,需比較n+1次;

時間複雜度:O(n)

空間複雜度:一個輔助空間—O(1)

順序查詢的特點:

優點:演算法簡單,邏輯次序無要求,且不同儲存結構均適用。

缺點:ASL太長,時間效率太低。

2.2折半查詢(二分或對分查詢)

折半查詢:每次將待查記錄所在區間縮小一半。

折半查詢演算法(非遞迴演算法):

1.設表長為n,low、high和mid分別指向待查元素所在區間的上界、下界和中點,key為給定的要查詢的值

2.初始時,令low=1,high=n,mid=(low+high)/2的向下取整

3.讓k與mid指向的記錄比較

若key==R[mid].key,查詢成功

若key<R[mid].key,則high=mid-1

若key>R[mid].key,則low=mid+1

4.重複上述操作,直至low>high時,查詢失敗

int Search_Bin(SSTable ST, KeyType key)
{
	low = 1;//置區間初值
	high = ST.length;
	while (low <= high)
	{
		mid = (low + high) / 2;
		if (ST.R[mid].key == key)//找到待查元素
			return mid;
		else if (key < ST.R[mid].key)//縮小查詢區間
			high = mid - 1;//繼續在前半區間進行查詢
		else
			low = mid + 1;//繼續在後半區間進行查詢
	}
	return 0;//順序表中不存在待查元素
}

折半查詢演算法(遞迴演算法):

int Search_Bin(SSTable ST, KeyType key,int low, int high)
{
	if (low > high)//查詢補刀時返回0
		return 0;
	mid = (low + high) / 2;
	if (key == ST.R[mid].key)
		return mid;
	else if (key < ST.R[mid].key)
		Search_Bin(ST, key, low, mid - 1);
	else
		Search_Bin(ST, key, mid + 1, high);
	return 0;
}

折半查詢的效能分析

折半查詢優點:效率比順序查詢高。

這般查詢缺點:只適用於有序表,且限於順序儲存結構(對線性連結串列無效)。

2.3分塊查詢(索引順序查詢)


3.樹表的查詢

3.1.二叉排序樹:

 

二叉排序樹的操作—查詢

typedef struct
{
	KeyType key;//關鍵字項
	InfoType otherinfo;//其他資料域
}ElemType;

typedef struct BSTNode
{
	ElemType data;//資料域
	struct BSTNode* lchild, * rchild;//左右孩子指標
}BSTNode, * BSTree;

BSTree T;//定義二叉排序樹T

二叉排序樹的遞迴查詢

BSTree SearchBST(BSTree T, KeyType key)
{
	//二叉排序樹的遞迴查詢
	if ((!T) || key == T->data.key)
		return T;
	else if (key < T->data.key)
		return SearchBST(T->lchild, key);//在左子樹中繼續查詢
	else
		return SearchBST(T->rchild, key);//在右子樹中繼續查詢
}

二叉排序樹的查詢分析

二叉排序樹的查詢分析

二叉排序樹的操作—插入

二叉排序樹的操作—生成

從空樹出發,經過一系列的查詢、查詢操作後,可生成一棵二叉排序樹。

例:設查詢的關鍵字序列為{45,24,53,45,12,24,90},可生成二叉排序樹如下:

一個無序序列可通過構造二叉樹變程一個有序序列。構造樹的過程就是對無序序列進行排序的過程。

插入的結點均為葉子結點,故無序移動其他結點。相當於在有序序列上插入記錄而無需移動其他記錄。

但是,關鍵字的輸入順序不同,建立的二叉排序樹不同。

二叉排序樹的操作—刪除

從二叉排序樹中刪除一個結點,不能把以該結點為根的子樹都刪除,只能刪掉該結點,並且還應保證刪除後所得的二叉樹仍然滿足二叉排序樹的性質不變

由於中序遍歷二叉排序樹可以得到一個遞增有序的序列。那麼,在二叉排序樹中刪去一個結點相當於刪去有序序列中的一個結點。

1.將因刪除結點而斷開的二叉連結串列重新連結起來。

2.防止重新連結後樹的高度增加。

總結:

3.2.平衡二叉樹

平衡二叉樹的定義:

又稱AVL

一棵平衡二叉樹或者是空樹,或者是具有以下性質的二叉排序樹

1.左子樹與右子樹的高度之差的絕對值小於等於1

2.左子樹右子樹也是平衡二叉排序樹。

為了方便起見,給每個結點附加一個數字,給出該結點左子樹與右子樹的高度差。這個數字稱為結點的平衡因子(BF)。

平衡因子=結點左子樹的高度-結點右子樹的高度

根據平衡二叉樹的定義,平衡二叉樹上所有結點的平衡因子只能是-101

失衡二叉排序樹的分析與調整

調整前先計算哪個結點是失衡結點

如果在一棵AVL樹中插入一個新結點後造成失衡,則必須重新調整樹的結構,使之恢復平衡

平衡調整的四種型別:

(1)LL型調整

AVL樹LL調整—例子:

(2)RR型調整

AVL樹RR調整—例子:

(3)LR型調整

AVL樹LR調整—例子:

(4)RL型調整

AVL樹RL調整—例子:

例題:

輸入關鍵字序列(16,3,7,11,9,26,18,14,15)給出構造一棵AVL樹的步驟。

   

   

3.3.B-樹

結點個數最多為階數-1,此題為5-1=4

結點個數最少為m/2取上限-1,此題為2

新增結點都是往葉子結點上新增

B-樹線是連在縫中的

用以下關鍵字序列{1,2,6,7,11,4,8,13,10,5,17,9,16,20,3,12,14,18,19,15}建立一棵5階B-樹

對於該B-樹,給出刪除關鍵字8、16、15、4的過程。

 

 

3.4.B+樹

B+樹線是連在結點上的

 


4.雜湊表的查詢

雜湊表的基本概念

基本思想:記錄的儲存位置域關鍵字之間存在對應的關係。

對應關係—hash函式

Loc(i)=H(keyi)

優點:查詢效率高  

缺點:空間效率低

雜湊表的若干術語

雜湊方法(雜湊法):

選取某個函式,依該函式按關鍵字計算元素的儲存位置,並按此存放;

查詢時,由同一個函式對給定值k計算地址,將k與地址單元中元素關鍵碼進行對比,確定查詢是否成功。

雜湊函式(雜湊函式):

雜湊方法中使用的轉換函式

雜湊表(雜湊表):

按照上述思想構造的表。

雜湊函式:H(key)=k

衝突:

不同的關鍵碼對映到同一個雜湊地址 key1≠key2,但是H(key1)=H(key2)。

同義詞:

具有相同函式值的多個關鍵字。

雜湊函式的構造方法

雜湊儲存:

選取某個函式,依該函式按關鍵字計算元素的儲存位置

在雜湊查詢法中,衝突是不可能避免的,只能儘可能減少。

1.直接定址法

Hash(key) = a*key + b   (a,b為常數)

優點:以關鍵碼key的某個線性函式值為雜湊地址,不會產生衝突。

缺點:要佔用連續地址空間,空間效率低

例:{100,300,500,700,800,900},

雜湊函式 Hash(key) = key/100  (a=1/100,b=0)

2.除留餘數法

Hash(key) = key mod p  (p是一個整數)

技巧:設表長為m,取p≤m且為質數

例:{15,23,27,38,53,61,70},

雜湊函式 Hash(key) = key mod 7

處理衝突的方法

1.開放地址法(開地址法)

基本思想:有衝突時就去尋找下一個空的雜湊地址,只要雜湊表足夠大,空的雜湊地址總能找到,並將資料元素存入。

1.1線性探測法

例:關鍵碼集為{47,7,9,11,16,92,22,8,3},雜湊表的表長為11;雜湊函式為Hash(key) = key mod 11;擬用線性探測法處理衝突。建雜湊表如下:

解釋:

①.47、7均是由雜湊函式得到的沒有衝突的雜湊地址;

②.Hash(29)=7,雜湊地址有衝突,需尋找下一個空的雜湊地址:

由H1=(Hash(29)+1) mod 11 =8,雜湊地址8為空,因此將29存入。

③.11、16、92均是由雜湊函式得到的沒有衝突的雜湊地址;

④.22、8、3(3的di為3)的情況同②

平均查詢長度ASL=(1+2+1+1+1+4+1+2+2)/9=1.67

1.2.二次探測法

關鍵碼集為{47,7,29,11,16,92,22,8,3}

設:雜湊函式為Hash(key) = key mod 11

其中:m為雜湊表長度,m要求是某個4k+3的質數;

           di為增量序列

1.3.偽隨機探測法

   (1 ≤ i < m)

其中:m為雜湊表長度

           di為偽隨機數

2.鏈地址法(拉鍊法)

基本思想:相同雜湊地址的記錄鏈成一單連結串列,m個雜湊地址就設m個單連結串列,然後用一個陣列將m個單連結串列的表頭指標儲存起來,形成一個動態的結構。

鏈地址法建立雜湊表步驟:

Step1:取資料元素的關鍵字key,計算其雜湊函式值(地址)。若該地址對於的連結串列為空,則將該元素插入此連結串列;否則執行Step2解決衝突。

Step2:根據選擇的衝突處理方法,計算關鍵字key的下一個儲存地址。若該地址對於的連結串列不為空,則利用連結串列的前插法或後插法將該元素插入此連結串列。

鏈地址法的優點:

1.非同義詞不會衝突,無“聚集”現象。

2.連結串列上的結點空間動態申請,更適合於表長不確定的情況。

雜湊表的查詢

例:已知一組關鍵字{19,14,23,1,68,20,84,27,55,11,10,79},雜湊函式為:H(key) = key mod 13,雜湊表長為m = 16,設每個記錄的查詢概率相等。

(1)用線性探測再雜湊處理衝突,即

H(19) = 6

H(14) = 1

H(23) = 10

H(1) = 1     衝突,H(1)=(1 + 1) MOD 13 = 2

H(68) = 3

H(20) = 7

H(84) = 6     衝突,H(84)=(84 + 1) MOD 13 = 7

                    衝突,H(84)=(84 + 2) MOD 13 = 8

H(27) = 1     衝突,H(27)=(27 + 1) MOD 13 = 2

                    衝突,H(27)=(27 + 2) MOD 13 = 3

                    衝突,H(27)=(27 + 3) MOD 13 = 4

......

ASL = (1*6 + 2 + 3*3 + 4 + 9)/12 = 2.5

(2)鏈地址法處理衝突

對於關鍵字集{19,14,23,1,68,20,84,27,55,11,10,79},n=12

無序表查詢ASL=(n+1)/2=6.5

有序表折半查詢ASL=≈2.7

雜湊表上查詢ASL≠O(1)

雜湊表的查詢效率分析

使用平均查詢長度ASL來衡量查詢演算法,ASL取決於

1.雜湊函式

2.處理衝突的方法

3.雜湊表的填裝因子α=表中填入的記錄數/雜湊表的長度

(α越大,表中記錄數越多,說明表裝得越滿,發生衝突的可能性就越大,查詢時比較次數就越多)

ASL與填裝因子α有關!既不是嚴格的O(1),也不是O(n)

結論

雜湊表技術具有很好的平均效能,優於一些傳統的技術;

鏈地址法優於開地址法;→動態

除留餘數法作雜湊函式優於其他型別函式。(取小於等於表長的質數,獲得比較好的雜湊效果)


 

相關文章