first edit:20170702
last edit:20170704
一、概念
- 查找表:同一類型數據元素的集合
- 關鍵字:數據元素中某個數據項的值
- 主關鍵字:可以唯一地標識一個記錄的關鍵字。如成績表中學生學號
- 次關鍵字:可以識別多個數據元素的關鍵字。如成績表中學生總成績
- 靜態查找表:只作查找操作
- 動態查找表:查找過程中插入表中不存在元素,或者從表中刪除已經存在的某個元素
二、順序表查找
- 順序查找
- 思想
從表中首個元素開始逐個查找 - 實現
int sequentialSearch(int *a, int n, int key) {
// a為數組,n為要查找元素的個數,key為要查找的關鍵字
// 查找成功返回匹配元素位置,否則返回INFINITY
for (int i = 0; i < n; i++)
if (a[i] == key)
return i;
return INFINITY;
}
- 分析
-- 復雜度O(n)
-- 可以將被查找概率較大的元素放在前面,不常用記錄放在后面,這樣可以提高實際使用時的效率
三、有序表查找
- 二分查找
- 思想
在有序表中,取中間記錄作為比較對象。每次未命中比較都會將搜索范圍減半 - 實現
// 二分查找
int binarySearch(int *a, int n, int key) {
// a為有序數組,n為要查找元素的個數,key為要查找的關鍵字
// 查找成功返回匹配元素位置,否則返回INFINITY
int low = 0, high = n - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (key < a[mid])
high = mid - 1;
else if (key > a[mid])
low = mid + 1;
else
return mid;
}
return INFINITY;
}
- 分析
-- 復雜度 O(logn)
-- 要求查找表內元素有序
-- 對靜態查找表,該算法較好
-- 對于需要頻繁執行插入或刪除操作的動態查找表,維護表內元素有序的代價較高
- 插值查找
-
思想
二分查找每次從中間查找并非最優→例如,在字典中查找apple,會從靠前的位置先查找→更新mid的計算公式為
大話數據結構p302→這樣能快速的使查找范圍逼近待查找值
- 實現
int insertSearch(int *a, int n, int key) {
// a為有序數組,n為要查找元素的個數,key為要查找的關鍵字
// 查找成功返回匹配元素位置,否則返回INFINITY
int low = 0, high = n - 1;
while (low <= high) {
int mid = low + (high - low)*(key - a[low]) / (a[high] - a[low]);// 插值計算公式
if (key < a[mid])
high = mid - 1;
else if (key > a[mid])
low = mid + 1;
else
return mid;
}
return INFINITY;
}
3.分析
-- 復雜度仍為O(logn)
-- 但對于表長較大,且關鍵字分布較均勻的查找表來說。平均性能要優于二分查找
- 裴波那契查找
- 思想
- 實現
- 分析
四、線性索引查找
- 概念
- 索引:把每個關鍵字和它對應的記錄相關聯的過程
- 線性索引:將索引項集合組織為線性結構
- 稠密索引:在線性索引中,每個元素對應一個索引項→索引項按關鍵碼有序排列→查找快,但索引量大。
- 分塊索引:對數據集進行分塊,使其塊間有序,塊內無序→索引項包括三個數據項:最大關鍵碼,塊中的記錄個數→指向塊首元素數據的指針→**兼顧了減少索引項數目和查找速度
- 倒排索引:查找具有相同次關鍵字的所有記錄的記錄號→索引項包括兩個數據項:次關鍵嗎、記錄號表→優點是查找快,缺點是記錄號表不定長
五、二叉排序樹
- 概念
二叉排序樹/二叉查找樹,或者是一顆空樹,或者具有以下性質:- 若它的做子樹不為空,則左子樹上所有節點均小于它的根結構的值;
- 若右子樹不為空,則右子樹上所有節點均大于它的根節點的值;
- 左右子樹也是二叉排序樹
- 中序遍歷二叉排序樹,得到有序序列
- 同時兼顧插入刪除和查找操作的速度
- 查找操作
- 遞歸調用查找函數,程序調用方法為SearchBST(v,key,NULL,NULL)→初始的father和result為NULL→如果是寫成BST類的方法,那么應該是返回result,father可以作為類的一個私有成員
bool SearchBST(BiTree v, int key,BiTree father, BiTree &result){
// 查找成功→返回true,result保存指向命中節點的指針
// 查找失敗→返回false,result保存指向查找路徑上訪問的最后一個節點
// 如果是空樹的話,result也是空指針
if (!v) {
result = father;
return false;
}
else if (key == v->data) {
result = v;
return true;
}
else
SearchBST(((key < v->data ? v->lchild : v->rchild)), key, v,result);
}
- 插入操作
- 先用查找操作找到合適的插入位置,注意新建分配節點
bool InsertBST(BiTree &v, int key) {
// 當二叉樹v中不存在關鍵字key時,插入并返回true
// 當二叉樹v中存在關鍵字key時,返回false
BiTree p = NULL;
if (!SearchBST(v, key, NULL, p)) { //查找不成功
// 這里就體現出了p的作用,p為查找路徑上訪問的最后一個節點,
// 同時,p節點肯定是有一個子節點是空的,所以key的插入后即為p的子節點
BiTNode *s = new BiTNode;
s->data = key;
s->lchild = NULL; s->rchild = NULL;
// 注意如果當且僅當v是空樹,p為空,此時新建節點為跟節點
if (!p)
v = s;
else if (key < p->data)
p->lchild = s;
else
p->rchild = s;
return true;
}
else
return false;
}
- 刪除操作
- 使用delete(p)刪除需要刪除的節點,因為所有節點都是用new新建的
- 考慮四種情況:
- 不存在待刪除的節點→返回false
- 待刪除節點為葉子節點→直接刪除
- 只有左子樹或只有右子樹→刪除原有節點,并用左子樹替代
-
左右子樹都存在→先找到左子樹中最大的值,替代該節點的值;刪除左子樹中最大節點,然后重接其父節點的左右子樹→**注意要考慮待刪除節點的左子節點就沒有右子節點的情況
image.png
待刪除節點的左子節點就沒有右子節點的情況
bool deleteBST(BiTree &v) {
if (v->rchild == NULL) {
BiTree q = v;
v = v->rchild;
delete(q);
}
else if (v->lchild == NULL){
BiTree q = v;
v = v->rchild;
delete(q);
}
else {
BiTree q = v;
BiTree s = v->lchild; //轉左,然后向右到盡頭,這樣就是左子樹中最大的
while (s->rchild) {
q = s; // q指向左子樹最大節點的父節點
s = s->rchild; // s指向左子樹中最大的節點
}
v->data = s->data;
if (q != v)
q->rchild = s->lchild;
else // 如果v的左子節點就沒有右節點
q->lchild = s->lchild;
delete(s);
}
return true;
}
bool DeleteBST(BiTree &v, int key) {
if (!v) //不存在關鍵字等于key的節點
return false;
else {
if (key == v->data)
return deleteBST(v);
else if (key < v->data)
return DeleteBST(v->lchild, key);
else
return DeleteBST(v->rchild, key);
}
}