一、概念1.定義與性質(1)設x為二叉尋找樹中的一個結點,若y是x左子樹中的一個結點,則key[y] <= key[x];若y是x右子樹中的一個結點,則key[x]<=key[y](2)二叉尋找樹上執行的基本操作的時間與樹的高度成正比。2.結構(1)結點結構:關鍵字key衛星資料data分別指向父、左右孩子的指標p, left, right3.在二叉尋找樹上的操作尋找一個關鍵字:SEARCH(x, k)求最小關鍵字:MINIMUM(x)求最大關鍵字:MAXIMUM(x)求前驅:PREDECESSOR(x)求後繼:SUCCESSOR(x)插入一個結點:INSERT(T, z)刪除一個結點:DELETE(z)4.二叉尋找樹的應用
1.遍曆:中序遍曆、先序遍曆、後序遍曆
2.尋找:尋找包含某個關鍵字的結點,尋找關鍵字最大或最小的結點、尋找某個結點的前驅或後繼
二、代碼
#include <iostream>#include <string>using namespace std;struct BST_Node{public:int key;//關鍵字int data;//衛星資料BST_Node *left;//左孩子BST_Node *right;//右孩子BST_Node *p;//父結點BST_Node(int x):key(x),data(x),left(NULL),right(NULL),p(NULL){}};//二叉尋找樹的結構class BST_Tree{public:BST_Node *root;BST_Tree():root(NULL){}public://12.1 二叉尋找樹void Inorder_Tree_Walk(BST_Node *x);//12.2 查詢二叉尋找樹BST_Node *Tree_Search(BST_Node *x, int k);BST_Node *Iterative_Tree_Search(BST_Node *x, int k);BST_Node *Iterative_Tree_Minimum(BST_Node *x);BST_Node *Iterative_Tree_Maximum(BST_Node *x);BST_Node *Tree_Successor(BST_Node *x);//12.3 插入和刪除void Iterative_Tree_Insert(BST_Node *z);BST_Node *Tree_Delete(BST_Node *z);};/*************12.1 二叉尋找樹****************************************************************///遞迴的中序遍曆void BST_Tree::Inorder_Tree_Walk(BST_Node *x){if(x != NULL){//中序遍曆當前結點的左子樹Inorder_Tree_Walk(x->left);//訪問當前結點cout<<x->key<<endl;//中序遍曆當前結點的右子樹Inorder_Tree_Walk(x->right);}}/********12.2 查詢二叉尋找樹****************************************************************///遞迴地查詢二叉尋找樹BST_Node *BST_Tree::Tree_Search(BST_Node *x, int k){//找到葉子結點了還沒找到,或當前結點是所尋找的結點if(x == NULL || k == x->key)return x;//所尋找的結點位於當前結點的左子樹if(k < x->key)return Tree_Search(x->left, k);//所尋找的結點位於當前結點的左子樹elsereturn Tree_Search(x->right, k);}//非遞迴的查詢二叉尋找樹BST_Node *BST_Tree::Iterative_Tree_Search(BST_Node *x, int k){//不是葉子結點且不是所尋找的結點while(x != NULL && k != x->key){//所尋找的結點位於當前結點的左子樹if(k < x->key)x = x->left;//所尋找的結點位於當前結點的右子樹elsex = x->right;}return x;}//非遞迴找最小值BST_Node *BST_Tree::Iterative_Tree_Minimum(BST_Node *x){//只要有比當前結點小的結點while(x->left != NULL)x = x->left;return x;}//非遞迴找最大值BST_Node *Iterative_Tree_Maximum(BST_Node *x){//只要有比當前結點小的結點while(x->right != NULL)x = x->right;return x;}//尋找中序遍曆下x結點的後繼,後繼是大於key[x]的最小的結點BST_Node *BST_Tree::Tree_Successor(BST_Node *x){//如果有右孩子if(x->right != NULL)//右子樹中的最小值return Iterative_Tree_Minimum(x->right);//如果x的右子樹為空白且x有後繼y,那麼y是x的最低祖先結點,且y的左兒子也是BST_Node *y = x->p;while(y != NULL && x == y->right){x = y;y = y->p;}return y;}/*********12.3插入和刪除**********************************************************///二叉尋找樹的插入,非遞迴void BST_Tree::Iterative_Tree_Insert(BST_Node *z){//找到要插入的位置BST_Node *x = root, *y = NULL;//若x為空白,x是要插入的位置,x的父是z->pwhile(x != NULL){y = x;if(z->key == x->key){cout<<"error:exist"<<endl;return;}if(z->key < x->key)x = x->left;elsex = x->right;}//修改指標,注意樹為空白的情況z->p = y;if(y == NULL)root = z;else if(z->key < y->key)y->left = z;else y->right = z;}//二叉尋找樹的刪除,實際刪除的不一定是z,可能是z的後繼//然後把z的值改為實際刪除結點的值BST_Node *BST_Tree::Tree_Delete(BST_Node *z){BST_Node *x, *y;//若z最多隻有一個孩子,實際刪除的結點是zif(z->left == NULL || z->right == NULL)y = z;//若z有兩個孩子,實際刪除的結點是z的後繼elsey = Tree_Successor(z);//用x表示"實際要刪除的結點"的孩子(最多一個孩子)if(y->left != NULL)x = y->left;elsex = y->right;//修改指標,以刪去結點if(x != NULL)//若"實際要刪除的結點"沒有孩子x->p = y->p;if(y->p == NULL)//若"實際要刪除的結點"是根結點root = x;else if(y == y->p->left)y->p->left = x;else y->p->right = x;//"若初階要刪除的結點"不是"待刪除的結點",則內容替代if(y != z){z->key = y->key;z->data = y->data;}return y;}三、練習12.1 二叉尋找樹
12.1-2
二叉尋找樹:左子樹關鍵字<根結點關鍵字<右子樹關鍵字
堆:左子樹關鍵字<根結點關鍵字 && 右子樹關鍵字<根結點關鍵字
不能,因為一個結點的的左子樹與右子樹的關鍵字大小沒有關係
12.1-3
用棧實現:見演算法導論-10.4-有根樹的表示中的10.4-3
不用棧實現:見演算法導論-10.4-5
12.1-4
//遞迴的先序遍曆 void BST_Tree::Preorder_Tree_Walk(BST_Node *x) { //x不是葉子結點 if(x != NULL) { //訪問當前結點 cout<<x->key<<' '; //先序遍曆當前結點的左子樹 Preorder_Tree_Walk(x->left); //先序遍曆當前結點的右子樹 Preorder_Tree_Walk(x->right); } }//遞迴的後序遍曆 void BST_Tree::Postorder_Tree_Walk(BST_Node *x) { //x不是葉子結點 if(x != NULL) { //後序遍曆當前結點的左子樹 Postorder_Tree_Walk(x->left); //後序遍曆當前結點的右子樹 Postorder_Tree_Walk(x->right); //訪問當前結點 cout<<x->data<<' '; } }
12.2 查詢二叉尋找樹
12.2-1c12.2-2//遞迴地尋找最小值 BST_Node *BST_Tree::Tree_Minimum(BST_Node *x) { if(x->left != NULL) return Tree_Minimum(x->left); else return x; } //遞迴的尋找最大值 BST_Node *BST_Tree::Tree_Maximum(BST_Node *x) { if(x->right != NULL) return Tree_Maximum(x->right); else return x; } 12.2-3//尋找中序遍曆下x的前驅,即小於x的最大值 BST_Node *BST_Tree::Tree_Predecessor(BST_Node *x) { //如果x的左子樹非空 if(x->left != NULL) //x的前驅是x的左子樹的最大值 return Tree_Maximum(x->left); //如果x的左子樹為空白且x有前驅y,那麼y是x的最低祖先結點,且y的右兒子也是 BST_Node *y = x->p; while(y != NULL && x == y->left) { x = y; y = y->p; } return y; } 12.2-4(1)4->left = 2 4->right =NIL2->left = 1 2->right = 3搜尋路徑4-2-1(2)1->right = 3 1->left = NUL3->left = 2 3->right = 4搜尋路徑1-3-412.3 插入和刪除
12.3-1
//遞迴的二叉尋找樹的插入操作,分三種情況 void BST_Tree::Tree_Insert(BST_Node *x, BST_Node *z) { //已經存在 if(z->key == x->key) { cout<<"error:exist"<<endl; return; } //插入到x的左子樹中 else if(z->key < x->key) { //x沒有左子樹 if(x->left == NULL) { //修改指標,插入操作 x->left = z; z->p = x; return; } //x有左子樹 else //對x的左子樹執行插入操作 Tree_Insert(x->left, z); } //插入到x的右子樹中,與上面類似 else if(z->key > x->key) { if(x->right == NULL) { x->right = z; z->p = x; } else Tree_Insert(x->right, z); } }
12.3-3
最壞是n^2
最好是nlgn
12.3-4
求y的前驅z分為兩種情況,以下分別討論:
(1)y有左孩子,則z是left[y]中最右邊的結點,z沒有右孩子,因此刪除z時直接刪除修改指標即可,沒有問題
(2)y沒有左孩子,則z是y的祖先,y是z右子樹是最左邊的點,又分為兩種情況:
(2.1)若z沒有左孩子,則直接刪除z並修改指標,沒有問題。
(2.2)若z有左孩子,則不直接刪除z,而是用z代替y存在並刪除y。這裡會有問題,另一個資料結構中的儲存了指向y的指標,但是y的內容轉移到另一個結點上了,指向y的指標指向了一個被釋放的空間。
解決方案:使TREE-DELETE返回刪除後的y的指標,這個值可能會變,可能不變。讓另一個資料結構的y指標指向TREE-DELETE的傳回值。
12.3-5
不或交換,反例
12.3-6
當待刪除結點有兩個子樹時,不刪除待刪除結點,而是刪除它的前驅或後繼,用隨機數rand()%2來確定刪除的前驅還是後繼
代碼見文:二
四、思考題12-1 具有相同關鍵字元素的二叉樹
見演算法導論-12-1-具有相同關鍵字元素的二叉尋找樹
12-2 基數樹
見演算法導論-12-2-基數樹
12-3 隨機構造的二叉尋找樹中的平均結點深度
f)待解決