目錄
一.查找概論
????1.概念:
????2.查找表按照操作方式來分有兩大種:靜態(tài)查找表和動態(tài)查找表
????3.面向查找操作的數(shù)據(jù)結(jié)構(gòu)稱為查找結(jié)構(gòu)
二.順序表查找
????1.順序查找(Sequential Search):又叫線性查找,是最基本的查找技術(shù)
????2.順序表查找算法:
????3.順序表查找算法優(yōu)化
三.有序表查找
????1.折半查找
????2.差值查找
????3.斐波那契查找
????4.三種有序表查找的區(qū)別在于:
四.線性索引查找
????1.含義
????2.稠密索引
????3.分塊索引
????4.倒排索引
五.二叉排序樹
????1.含義:
????2.二叉排序樹查找操作
????3.二叉排序樹插入操作
????4.二叉排序樹的刪除操作
????5.二叉排序樹總結(jié)
六.平衡二叉樹
????1.含義
????2.平衡二叉樹的實現(xiàn)原理
????3.平衡二叉樹實現(xiàn)算法
七.多路查找樹(B樹)
????1.含義
????2.2-3樹
????3.2-3-4樹
????4.B樹
????5.B+樹
八.散列表查找(哈希表)概述
????1.散列表查找定義
????2.散列表查找步驟
????3.散列主要是面向查找的存儲結(jié)構(gòu)
九.散列函數(shù)的構(gòu)造方法
????1.好的散列函數(shù)的兩個原則
????2.直接定址法
????3.數(shù)字分析法
????4.平方取中法
????5.折疊法
????6.除留余數(shù)法
????7.采用不同散列函數(shù)的因素
十.處理散列沖突的方法
????1.開放定址法
????2.再散列函數(shù)法
????3.鏈地址法
????4.公共溢出區(qū)法
十一.散列表查找實現(xiàn)
????1.散列表查找算法實現(xiàn)
????2.散列表查找性能分析
一.查找概論
1.概念:
(1)查找表(Search Table)
是由同一類型的數(shù)據(jù)元素(或記錄)構(gòu)成的集合
(2)關(guān)鍵字(Key)
是數(shù)據(jù)元素中某個數(shù)據(jù)項的值,又稱為鍵值,用它可以標識一個數(shù)據(jù)元素。也可以標識一個記錄的某個數(shù)據(jù)項(字段),稱為關(guān)鍵碼
(3)主關(guān)鍵字:
若此關(guān)鍵字可以唯一地標識一個記錄,則稱此關(guān)鍵字為主關(guān)鍵字(Primary Key)
(4)次關(guān)鍵字:
對于那些可以識別多個數(shù)據(jù)元素(或記錄)的關(guān)鍵字,我們稱為次關(guān)鍵字,次關(guān)鍵字也可以理解為是不以唯一標識一個數(shù)據(jù)元素(或記錄)的關(guān)鍵字,它對應(yīng)的數(shù)據(jù)項就是次關(guān)鍵碼
2.查找表按照操作方式來分有兩大種:靜態(tài)查找表和動態(tài)查找表
(1)靜態(tài)查找表(Static Search Table):只作查找操作的查找表。
它的主要操作有:
- 查詢某個“特定的”數(shù)據(jù)元素是否在查找表中
- 檢索某個“特定的”數(shù)據(jù)元素和各種屬性
(2)動態(tài)查找表(Dynamic Search Table):
在查找過程中同時插入查找表中不存在的數(shù)據(jù)元素,或者從查找表中刪除已經(jīng)存在的某個數(shù)據(jù)元素
動態(tài)查找表的操作就是兩個:
- 查找時插入數(shù)據(jù)元素
- 查找時刪除數(shù)據(jù)元素
3.面向查找操作的數(shù)據(jù)結(jié)構(gòu)稱為查找結(jié)構(gòu)
二.順序表查找
1.順序查找(Sequential Search):又叫線性查找,是最基本的查找技術(shù)
2.順序表查找算法:
3.順序表查找算法優(yōu)化:
三.有序表查找
1.折半查找
(1)折半查找(Binary Search)技術(shù),又稱為二分查找。
它的前提是線性表中的記錄必須是關(guān)鍵碼有序(通常從小到大有序),線性表必須采用順序存儲。
(2)折半查找的基本思想是:
在有序表中,取中間記錄作為比較對象,若給定值與中間記錄的關(guān)鍵字相等,則查找成功;若給定值小于中間記錄的關(guān)鍵字,則在中間記錄的左半?yún)^(qū)繼續(xù)查找;若給定值大于中間記錄的關(guān)鍵字,則在中間記錄的右半?yún)^(qū)繼續(xù)查找。不斷重復(fù)上述過程,直到查找成功,或所有查找區(qū)域無記錄,查找失敗為止
(3)折半算法的時間復(fù)雜度為O(logn)
2.插值查找
插值查找(Interpolation Search)是根據(jù)要查找的關(guān)鍵字key與查找表中最大最小記錄的關(guān)鍵字比較后的查找方法,其核心就在于插值的計算公式(key-a[low])/(a[high]-a[low])
3.斐波那契查找
(1)斐波那契查找(Fibonacci Search),是利用黃金分割原理來實現(xiàn)的
(2)“斐波那契查找算法的核心在于:
- 當key=a[mid]時,查找就成功;
- 當key<a[mid]時,新范圍是第low個到第mid-1個,此時范圍個數(shù)為F[k-1]-1個;
- 當key>a[mid]時,新范圍是第m+1個到第high個,此時范圍個數(shù)為F[k-2]-1個
4.三種有序表查找的區(qū)別在于:
- 折半查找是進行加法與除法運算(mid=(low+high)/2),
- 插值查找進行復(fù)雜的四則運算(mid=low+(high-low)*(key-a[low])/(a[high]-a[low])),
- 而斐波那契查找只是最簡單加減法運算(mid=low+F[k-1]-1)
四.線性索引查找
1.含義:
(1)索引是為了加快查找速度而設(shè)計的一種數(shù)據(jù)結(jié)構(gòu)。
索引就是把一個關(guān)鍵字與它對應(yīng)的記錄相關(guān)聯(lián)的過程,一個索引由若干個索引項構(gòu)成,每個索引項至少應(yīng)包含關(guān)鍵字和其對應(yīng)的記錄在存儲器中的位置等信息
(2)索引按照結(jié)構(gòu)可以分為線性索引、樹形索引和多級索引
(3)所謂線性索引就是將索引項集合組織為線性結(jié)構(gòu),也稱為索引表
2.稠密索引
(1)稠密索引是指在線性索引中,將數(shù)據(jù)集中的每個記錄對應(yīng)一個索引項
(2)索引項有序也就意味著,我們要查找關(guān)鍵字時,可以用到折半、插值、斐波那契等有序查找算法,大大提高了效率
3.分塊索引
(1)分塊有序,是把數(shù)據(jù)集的記錄分成了若干塊,并且這些塊需要滿足兩個條件:
- 塊內(nèi)無序,即每一塊內(nèi)的記錄不要求有序
- 塊間有序
(2)定義的分塊索引的索引項結(jié)構(gòu)分三個數(shù)據(jù)項:
- 最大關(guān)鍵碼,它存儲每一塊中的最大關(guān)鍵字,這樣的好處就是可以使得在它之后的下一塊中的最小關(guān)鍵字也能比這一塊最大的關(guān)鍵字要大;
- 存儲了塊中的記錄個數(shù),以便于循環(huán)時使用;
- 用于指向塊首數(shù)據(jù)元素的指針,便于開始對這一塊中記錄進行遍歷
(3)在分塊索引表中查找,就是分兩步進行:
- 一是在分塊索引表中查找要查關(guān)鍵字所在的塊
- 二是根據(jù)塊首指針找到相應(yīng)的塊,并在塊中順序查找關(guān)鍵碼
4.倒排索引
(1)倒排索引的索引項通用結(jié)構(gòu)是:
- 次關(guān)鍵碼,例如上面的“英文單詞”;
- 記錄號表,例如上面的“文章編號”。
(2)其中記錄號表存儲具有相同次關(guān)鍵字的所有記錄的記錄號,這樣的索引方法就是倒排索引
(3)由于不是由記錄來確定屬性值,而是由屬性值來確定記錄的位置,因而稱為倒排索引
五.二叉排序樹
1.含義:
(1)二叉排序樹(Binary Sort Tree),又稱為二叉查找樹。
它或者是一棵空樹,或者是具有下列性質(zhì)的二叉樹。
- 若它的左子樹不空,則左子樹上所有結(jié)點的值均小于它的根結(jié)構(gòu)的值;
- 若它的右子樹不空,則右子樹上所有結(jié)點的值均大于它的根結(jié)點的值;
- 它的左、右子樹也分別為二叉排序樹
2.二叉排序樹查找操作
(1)二叉樹結(jié)構(gòu)
/* 二叉樹的二叉鏈表結(jié)點結(jié)構(gòu)定義 */
/* 結(jié)點結(jié)構(gòu) */
typedef struct BiTNode
{
/* 結(jié)點數(shù)據(jù) */
int data;
/* 左右孩子指針 */
struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;
(2)二叉樹插入操作
/* 遞歸查找二叉排序樹T中是否存在key, */
/* 指針f指向T的雙親,其初始調(diào)用值為NULL */
/* 若查找成功,則指針p指向該數(shù)據(jù)元素結(jié)點,并
返回TRUE */
/* 否則指針p指向查找路徑上訪問的最后一個結(jié)點
并返回FALSE */
Status SearchBST(BiTree T, int key, BiTree f, BiTree *p)
{
/* 查找不成功 */
if (!T)
{
*p = f;
return FALSE;
}
/* 查找成功 */
else if (key == T->data)
{
*p = T;
return TRUE;
}
else if (key < T->data)
/* 在左子樹繼續(xù)查找 */
return SearchBST(T->lchild, key, T, p);
else
/* 在右子樹繼續(xù)查找 */
return SearchBST(T->rchild, key, T, p);
}
3.二叉排序樹插入操作
/* 當二叉排序樹T中不存在關(guān)鍵字等于key的數(shù)據(jù)元
素時, */
/* 插入key并返回TRUE,否則返回FALSE */
Status InsertBST(BiTree *T, int key)
{
BiTree p, s;
/* 查找不成功 */
if (!SearchBST(*T, key, NULL, &p))
{
s = (BiTree)malloc(sizeof(BiTNode));
s->data = key;
s->lchild = s->rchild = NULL;
if (!p)
/* 插入s為新的根結(jié)點 */
*T = s;
else if (key < p->data)
/* 插入s為左孩子 */
p->lchild = s;
else
/* 插入s為右孩子 */
p->rchild = s;
return TRUE;
}
else
/* 樹中已有關(guān)鍵字相同的結(jié)點,不再插入 */
return FALSE;
}
4.二叉排序樹的刪除操作:
(1)刪除結(jié)點三種情況:
- 葉子結(jié)點;
- 僅有左或右子樹的結(jié)點;
- 左右子樹都有的結(jié)點,我們來看代碼,下面這個算法是遞歸方式對二叉排序樹T查找key,查找到時刪除
5.二叉排序樹總結(jié):
(1)二叉排序樹是以鏈接的方式存儲,保持了鏈接存儲結(jié)構(gòu)在執(zhí)行插入或刪除操作時不用移動元素的優(yōu)點,只要找到合適的插入和刪除位置后,僅需修改鏈接指針即可
(2)一個集合按二叉排序樹查找,最好是把它構(gòu)建成一棵平衡的二叉排序樹
六.平衡二叉樹
1.含義:
(1)平衡二叉樹(Self-Balancing Binary SearchTree或Height-Balanced Binary Search Tree)
是一種二叉排序樹,其中每一個節(jié)點的左子樹和右子樹的高度差至多等于1
(2)要么它是一棵空樹,要么它的左子樹和右子樹都是平衡二叉樹
(3)距離插入結(jié)點最近的,且平衡因子的絕對值大于1的結(jié)點為根的子樹,我們稱為最小不平衡子樹
2.平衡二叉樹的實現(xiàn)原理:
(1)當最小不平衡子樹根結(jié)點的平衡因子BF是大于1時,就右旋,小于-1時就左旋
3.平衡二叉樹實現(xiàn)算法:
(1)二叉樹結(jié)構(gòu)定義
/* 二叉樹的二叉鏈表結(jié)點結(jié)構(gòu)定義 */
/* 結(jié)點結(jié)構(gòu) */
typedef struct BiTNode
{
/* 結(jié)點數(shù)據(jù) */
int data;
/* 結(jié)點的平衡因子 */
int bf;
/* 左右孩子指針 */
struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;
(2)右旋操作:
/* 對以p為根的二叉排序樹作右旋處理, */
/* 處理之后p指向新的樹根結(jié)點,即旋轉(zhuǎn)處理之前
的左子樹的根結(jié)點 */
void R_Rotate(BiTree *P)
{
BiTree L;
/* L指向P的左子樹根結(jié)點 */
L = (*P)->lchild;
/* L的右子樹掛接為P的左子樹 */
(*P)->lchild = L->rchild;
L->rchild = (*P);
/* P指向新的根結(jié)點 */
*P = L;
}
(3)左旋操作
/* 對以P為根的二叉排序樹作左旋處理, */
/* 處理之后P指向新的樹根結(jié)點,即旋轉(zhuǎn)處理之前
的右子樹的根結(jié)點0 */
void L_Rotate(BiTree *P)
{
BiTree R;
/* R指向P的右子樹根結(jié)點 */
R = (*P)->rchild;
/* R的左子樹掛接為P的右子樹 */
(*P)->rchild = R->lchild;
R->lchild = (*P);
/* P指向新的根結(jié)點 */
*P = R;
}
七.多路查找樹(B樹)
1.含義:
(1)多路查找樹(muitl-way search tree),
其每一個結(jié)點的孩子數(shù)可以多于兩個,且每一個結(jié)點處可以存儲多個元素。由于它是查找樹,所有元素之間存在某種特定的排序關(guān)系。
(2)四種特殊形式:2-3樹、2-3-4樹、B樹和B+樹
2.2-3樹
(1)含義:
2-3樹是這樣的一棵多路查找樹:其中的每一個結(jié)點都具有兩個孩子(我們稱它為2結(jié)點)或三個孩子(我們稱它為3結(jié)點)
(2)一個2結(jié)點包含一個元素和兩個孩子(或沒有孩子),
且與二叉排序樹類似,左子樹包含的元素小于該元素,右子樹包含的元素大于該元素,這個2結(jié)點要么沒有孩子,要有就有兩個,不能只有一個孩子
(3)一個3結(jié)點包含一小一大兩個元素和三個孩子(或沒有孩子),
如果某個3結(jié)點有孩子的話,左子樹包含小于較小元素的元素,右子樹包含大于較大元素的元素,中間子樹包含介于兩元素之間的元素
(4)2-3樹的插入實現(xiàn):
對于2-3樹的插入來說,與二叉排序樹相同,插入操作一定是發(fā)生在葉子結(jié)點上。可與二叉排序樹不同的是,2-3樹插入一個元素的過程有可能會對該樹的其余結(jié)構(gòu)產(chǎn)生連鎖反應(yīng)
(5)2-3樹插入分三種情況:
- 對于空樹,插入一個2結(jié)點即可
- 插入結(jié)點到一個2結(jié)點的葉子上。應(yīng)該說,由于其本身就只有一個元素,所以只需要將其升級為3結(jié)點即可
- 要往3結(jié)點中插入一個新元素。因為3結(jié)點本身已經(jīng)是2-3樹的結(jié)點最大容量(已經(jīng)有兩個元素),因此就需要將其拆分,且將樹中兩元素或插入元素的三者中選擇其一向上移動一層
(6)2-3樹的刪除實現(xiàn):
- 所刪除元素位于一個3結(jié)點的葉子結(jié)點上, 只需要在該結(jié)點處刪除該元素即可,不會影響到整棵樹的其他結(jié)點結(jié)構(gòu)
- 所刪除的元素位于一個2結(jié)點上,即要刪除的是一個只有一個元素的結(jié)點,分四種情形,情形一,結(jié)點的雙親也是2結(jié)點,且擁有一個3結(jié)點的右孩子。若要刪除結(jié)點1,那么只需要左旋即可;情形二,結(jié)點的雙親是2結(jié)點,它的右孩子也是2結(jié)點;情形三,此結(jié)點的雙親是一個3結(jié)點;情形四,當前樹是一個滿二叉樹的情況
- 所刪除的元素位于非葉子的分支結(jié)點。此時通常是將樹按中序遍歷后得到此元素的前驅(qū)或后繼元素,考慮讓它們來補位即可
3.2-3-4樹
(1)含義:
它其實就是2-3樹的概念擴展,包括了4結(jié)點的使用。一個4結(jié)點包含小中大三個元素和四個孩子(或沒有孩子),一個4結(jié)點要么沒有孩子,要么具有4個孩子。如果某個4結(jié)點有孩子的話,左子樹包含小于最小元素的元素;第二子樹包含大于最小元素,小于第二元素的元素;第三子樹包含大于第二元素,小于最大元素的元素;右子樹包含大于最大元素的元素
4.B樹
(1)含義:
結(jié)點最大的孩子數(shù)目稱為B樹的階(order),因此,2-3樹是3階B樹,2-3-4樹是4階B樹。
(2)一個m階的B樹具有如下屬性:
- 如果根結(jié)點不是葉結(jié)點,則其至少有兩棵子樹。
- 每一個非根的分支結(jié)點都有k-1個元素和k個孩子,其中。每一個葉子結(jié)點n都有k-1個元素,
- 所有葉子結(jié)點都位于同一層次。
- 所有分支結(jié)點包含下列信息數(shù)據(jù)
(3)在B樹上查找的過程是一個順指針查找結(jié)點和在結(jié)點中查找關(guān)鍵字的交叉過程
(4)至于B樹的插入和刪除,方式是與2-3樹和2-3-4樹相類似的,只不過階數(shù)可能會很大而已
5.B+樹
(1)含義:
B+樹是應(yīng)文件系統(tǒng)所需而出的一種B樹的變形樹
(2)在B樹中,每一個元素在該樹中只出現(xiàn)一次,有可能在葉子結(jié)點上,也有可能在分支結(jié)點上。而在B+樹中,出現(xiàn)在分支結(jié)點中的元素會被當作它們在該分支結(jié)點位置的中序后繼者(葉子結(jié)點)中再次列出。另外,每一個葉子結(jié)點都會保存一個指向后一葉子結(jié)點的指針
(3)一棵m階的B+樹和m階的B樹的差異在于:
- 有n棵子樹的結(jié)點中包含有n個關(guān)鍵字;
- 所有的葉子結(jié)點包含全部關(guān)鍵字的信息,及指向含這些關(guān)鍵字記錄的指針,葉子結(jié)點本身依關(guān)鍵字的大小自小而大順序鏈接;
- 所有分支結(jié)點可以看成是索引,結(jié)點中僅含有其子樹中的最大(或最小)關(guān)鍵字
(4)B+樹的插入、刪除過程也都與B樹類似,只不過插入和刪除的元素都是在葉子結(jié)點上進行而已
八.散列表查找(哈希表)概述
1.散列表查找定義:
(1)散列技術(shù)是在記錄的存儲位置和它的關(guān)鍵字之間建立一個確定的對應(yīng)關(guān)系f
(2)這種對應(yīng)關(guān)系f稱為散列函數(shù),又稱為哈希(Hash)函數(shù)
(3)按這個思想,采用散列技術(shù)將記錄存儲在一塊連續(xù)的存儲空間中,這塊連續(xù)存儲空間稱為散列表或哈希表(Hash table)。那么關(guān)鍵字對應(yīng)的記錄存儲位置我們稱為散列地址
2.散列表查找步驟:
(1)在存儲時,通過散列函數(shù)計算記錄的散列地址,并按此散列地址存儲該記錄
(2)當查找記錄時,我們通過同樣的散列函數(shù)計算記錄的散列地址,按此散列地址訪問該記錄
3.散列主要是面向查找的存儲結(jié)構(gòu)。
九.散列函數(shù)的構(gòu)造方法
1.好的散列函數(shù)的兩個原則:
(1)計算簡單
(2)散列地址分布均勻
2.直接定址法
3.數(shù)字分析法:
(1)抽取方法是使用關(guān)鍵字的一部分來計算散列存儲位置的方法,這在散列函數(shù)中是常常用到的手段
(2)數(shù)字分析法通常適合處理關(guān)鍵字位數(shù)比較大的情況,如果事先知道關(guān)鍵字的分布且關(guān)鍵字的若干位分布較均勻,就可以考慮用這個方法
4.平方取中法
5.折疊法:
(1)含義:
折疊法是將關(guān)鍵字從左到右分割成位數(shù)相等的幾部分(注意最后一部分位數(shù)不夠時可以短些),然后將這幾部分疊加求和,并按散列表表長,取后幾位作為散列地址
6.除留余數(shù)法:
此方法為最常用的構(gòu)造散列函數(shù)方法。對于散列表長為m的散列函數(shù)公式為:
f(key)=key mod p(p≤m)
mod是取模(求余數(shù))的意思
6.隨機數(shù)法:
7.采用不同散列函數(shù)的因素:
(1).計算散列地址所需的時間
(2).關(guān)鍵字的長度
(3).散列表的大小
(4).關(guān)鍵字的分布情況
(5).記錄查找的頻率
十.處理散列沖突的方法:
1.開放定址法
2.再散列函數(shù)法
3.鏈地址法
4.公共溢出區(qū)法
十一.散列表查找實現(xiàn)
1.散列表查找算法實現(xiàn)
2.散列表查找性能分析
(1)如果沒有沖突,散列查找是我們本章介紹的所有查找中效率最高的,因為它的時間復(fù)雜度為O(1)
(2)散列查找的平均查找長度取決于的因素
- 散列函數(shù)是否均勻
- 處理沖突的方法
- 散列表的裝填因子,所謂的裝填因子α=填入表中的記錄個數(shù)/散列表長度。α標志著散列表的裝滿的程度。當填入表中的記錄越多,α就越大,產(chǎn)生沖突的可能性就越大