二叉搜尋樹是樹的資料結構。這是乙個普通的二進位制最大的區別是二叉搜尋樹,以滿足物業:對於樹中的隨機的乙個節點。其左側的所有節點的子樹keyword值不超過該節點更大keyword值,節點的隨機右子樹keyword值不小於所述節點keyword值。
在二叉查詢樹上能夠進行搜尋、取最小值、取最大值、取指定節點的前驅、取指定節點的後繼以及插入和刪除節點操作,因此二叉查詢樹和堆(大頂堆和小頂堆)一樣。也能夠做優先佇列,都能夠在 o(lgn) 的時間內取得集合的最大值和最小值。
乙個二叉查詢樹的期望高度為o(lgn),因此在二叉查詢樹上的基本操作都能在期望時間o(lgn)下實現,可是最壞情況下時間為o(n),也就是二叉排序樹極度不平衡,變成一條鏈。對二叉查詢樹的前序、中序和後序遍歷均是在 o(n) 的時間複雜度內實現。
依據二叉查詢樹的性質,當中序遍歷的結果就是樹元素的按非遞減序輸出,因此研究二叉排序樹的中序遍歷及遍歷中元素的前驅和後繼是很重要的。
使用二叉查詢樹也有乙個問題。隨著插入和刪除操作的不斷執行。樹的高度就會發生變化,不一定為o(lgn),這也是它的乙個缺陷,因此在做優先佇列時。儘管堆(看做全然二叉樹)和查詢樹都是利用二叉樹的結構來實現的,可是用堆較查詢樹多。
以下的**是包含了二叉樹的前序、中序和後序遍歷的遞迴和非遞迴演算法,以及二叉查詢樹的相關操作。
/*
* 演算法導論 第十二章 二叉查詢樹
*/#include #include using namespace std;
//二叉樹節點定義
typedef struct tnode
tnode, tree;
/* * 前序遍歷
* 遞迴
*/void preordertreewalkrecursion(tree* tree)}/*
* 前序遍歷二叉樹
* 非遞迴
*/void preordertreewalknorecursion(tree* tree)
stacktreestack;
treestack.push(tree);
while (! treestack.empty())
treestack.pop();
if (! treestack.empty())
}}/*
* 中序遍歷
* 遞迴
*/void inordertreewalkrecursion(tree* tree)}/*
* 中序遍歷
* 非遞迴 用棧
*/void inordertreewalknorecursionstack(tree* tree)
treestack.pop();
if (! treestack.empty())
}}/*
* 中序遍歷
* 非遞迴 無棧
*/void inordertreewalknorecursionnostack(tree* tree)
if (! pleft->right)
else
} //在向下階段直到沒有左孩子,在向上回溯階段表示左子樹已經遍歷完
//此時應該遍歷此節點,然後遍歷其右孩子
couttree = tree->right; }}
/* * 後序遍歷
* 遞迴
*/void postordertreewalkrecursion(tree* tree)}/*
* 後序遍歷
* 非遞迴
*/void postordertreewalknorecursion(tree* tree)
treestack.pop();
if (! treestack.empty())
else
} }}/*
* 二叉排序樹的查詢
* 遞迴
* 時間複雜度為樹的高度o(lgn)
*/tnode* treesearch(tree* tree, int k)
if (k < tree->key)
else }/*
* 二叉排序樹查詢
* 非遞迴
* 時間複雜度為o(lgn)
*/tnode* treesearchiterative(tree* tree, int k)
else
} return tree;}/*
* 查詢二叉排序樹中的最小元素
* 即為最左元素
* 時間複雜度為o(lgn)
*/tnode* treeminimum(tree* tree)
return tree;}/*
* 查詢二叉查詢樹中的最大元素
* 即為最右元素
* 時間複雜度為o(lgn)
*/tnode* treemaximum(tree* tree)
return tree;}/*
* 求二叉排序樹中指定節點node的中序遍歷後繼
* 假設node右子樹不為空,則後繼則為node右子樹中的最小節點
* 否則node右子樹為空,必須向上回溯找到第乙個節點:該節點為其父節點的左孩子
* 後繼即為其父節點,假設不存在這樣的節點,說明node為最右節點,後繼為空
* 時間複雜度為o(lgn)
*/tnode* treesuccessor(tnode* node)
tnode* temp = node->parent;
while (temp && node == temp->right)
return temp;}/*
* 求二叉排序樹中指定節點node的中序遍歷前驅
* 假設node左子樹不為空,則前驅則為node左子樹中的最大節點
* 否則node左子樹為空,必須向上回溯找到第乙個節點:該節點為其父節點的右孩子
* 前驅即為其父節點。假設不存在這樣的節點,說明node為最左節點。前驅為空
* 時間複雜度為o(lgn)
*/tnode* treepredecessor(tnode* node)
tnode* temp = node->parent;
while (temp && node == temp->left)
return temp;}/*
* 二叉查詢樹插入元素
* 新元素僅僅能插入到二叉樹的葉子節點上
* 首先須要找到這個插入點
* 時間複雜度主要在搜尋插入位置上,為o(lgn)
*/void treeinsert(tree* &tree, tnode* node)
tnode* pos = null;
tnode* temp = tree;
while (temp)
else }
node->parent = pos;
if (! pos)
else if (node->key < pos->key) else }/*
* 二叉查詢樹刪除元素
* 刪除二叉查詢樹中的乙個元素必須繼續保持二叉查詢樹的性質。也就是保證中序遍歷的結果不變
* 依據被刪除節點分三種情況
* 1、刪除節點沒有孩子,這樣的情況下直接刪除該節點就可以
* 2、刪除節點有乙個孩子,這樣的情況先將節點刪除,再將該節點的孩子填補上來就可以
* 3、刪除節點有兩個孩子,這樣的情況相對麻煩一點,須要先找到刪除節點的後繼節點
* 然後將該刪除節點的資料值改動為後繼結點的值,然後將後繼節點刪除。因為後繼節點不可能有左孩子,否則刪除節點
* 的後繼就應該是後繼節點的左孩子。所以後繼節點的刪除非常easy
* 時間主要用在查詢刪除節點的後繼節點上,所以須要o(lgn)
*/tnode* treedelete(tree* &tree, tnode* node)
else
//找到替補節點
tnode* fillnode = null;//填補(到被刪除的位置)節點
if (delnode->left)
else
//將替補節點接到刪除節點的位置
//可能delnode沒有孩子
if (fillnode)
//delnode可能是根節點
if (! delnode->parent)
else if (delnode == delnode->parent->left) else
//假設實際刪除的是node的後繼節點,則複製資料
if (delnode != node)
return delnode;
}int main()
; int len = sizeof(arr) / sizeof(arr[0]);
tree* tree = null;
for (int i=0; ikey = arr[i];
node->left = node->parent = node->right = null;
treeinsert(tree, node);
} cout<<"前序遞迴遍歷二叉排序樹:"<<",中序後繼是:"<<(treesuccessor(pnode))->keypnode->left = pnode->parent = pnode->right = null;
treeinsert(tree, pnode);
cout<<"插入節點"
cout
pnode = treedelete(tree, pnode);
if (pnode)
cout<<"刪除節點"
cout
}
演算法導論 12章 二叉搜尋樹
最壞執行時間 完全二叉樹 o lgn 線性鏈 o n 隨機構造 o lgn 平均時間o lgn 二叉搜尋樹性質 左子樹小於雙親,右子樹大於雙親 二叉搜尋樹的基本操作所花費的時間與這棵樹的高度成正比。遍歷中序遍歷o n 子樹根在左右之間 偽 inorder tree walk x if x nil i...
演算法導論第12章 二叉搜尋樹
左子樹 根 右子樹 根據二叉樹的基本性質,向左子樹或右子樹遞迴即可 查詢結點x的後繼y分為兩種情況 右結點存在,即只需要找到右子樹中最小的元素就好了 右結點不存在,此時就要向親代查詢,直到找到右上方的親代,此時y是x的最底層祖先,且y的左孩子是x的乙個祖先。迭代演算法 類似於一根小棒在樹中移動,最終...
演算法導論筆記 12二叉搜尋樹
1 概念 二叉搜尋樹也叫二叉排序樹,它支援的操作有 search,minimum,maximum,predecessor,successor,insert,delete。所以,一顆二叉搜尋樹既可以作為乙個字典,又可以作為乙個優先佇列。二叉搜尋樹的基本操作時間與這棵樹的高度成正比。二叉搜尋樹的高度可以...