二叉查找樹(BST樹)


二叉查找樹的特點:

在二叉查找樹中左子樹上所有結點的數據都小於等於根結點的數據,而右子樹上所有結點的數據都大於根結點的數據

 1 //存儲結構:
 2 struct node
 3 {
 4     Int data;
 5     node *lchild;
 6     node *rchild;
 7 };
 8 
 9 //在建樹前根節點不存在:
10 Node *root = NULL;
11 
12 //新建結點:
13 node *newNode(int v)
14 {
15     node *Node = new node;
16     Node->data = v;
17     Node->lchild = NULL;
18     Node->rchild = NULL;
19     return Node;
20 }
21 
22 //二叉查找樹結點的查找、修改:
23 //時間復雜度:O(h) h是二叉查找樹的高度
24 void search(node *root,int x,int newdata) // 在這里修改的是root指針指向的內容,所以不需要加引用&
25 {
26     if(root == NULL)
27         return;
28     if(root->data == x)
29         root->data = newdata;
30     if(root->data <= x) search(root->lchild,x,newdata);
31     else if(root->data > x)
32         search(root->rchild,x,newdata);
33 }
34 //二叉查找樹的插入:
35 //時間復雜度:O(h) h是二叉查找樹的高度
36 void insert(node *&root,int x) // 這里要加引用,是因為修改的是root指針本身 
37 {
38     if(root == NULL)
39     {
40         root = newNode(x);
41         return root;
42     }
43     if(x<=root->data)   // 生成二叉查找樹 
44         insert(root->lchild,x);
45     else
46         insert(root->rchild,x);
47 }
48 
49 //二叉查找樹的創建:
50 node *create(int data[],int n)   
51 {
52     node *root = NULL;
53     for(int i=0;i<n;++i)
54         insert(root,data[i]);
55     return root;    
56 }

 

二叉查找樹的刪除
一般有兩種常見做法,時間復雜度都是$O(h)$,h是二叉查找樹的高度。為了保證刪除之后仍然是二叉查找樹。
一種方法是以樹中比刪去數小而又最大的結點(稱為該結點的前驅)覆蓋刪去數,再刪去該結點。
另一種則是以樹中比刪去數大而又最小的結點(稱為該結點的后繼)覆蓋刪去數,再刪去該結點。

 1 node *findMax(node *root)
 2 {
 3     while(root->rchild != NULL)
 4         root = root->rchild;
 5     return root;
 6 }
 7 
 8 node *findMin(node *root)
 9 {
10     while(root->lchild != NULL)
11         root = root->lchild;
12     return root;
13 }
14 
15 void deleteNode(node *&root,int x) // 加引用是因為要修改指針本身(刪除) 
16 {
17     if(root == NULL) // 樹中沒有x這個數據 
18         return;
19     if(root->data == x)
20     {
21         if(root->lchild==NULL && root->rchild==NULL)  //沒有左右子樹,直接刪除 
22         {
23             root = NULL;
24         }
25         else if(root->lchild!=NULL) // 有左子樹,則找前驅來覆蓋root 
26         {
27             node *pre = findMax(root->lchild);
28             root->data = pre->data;
29             deleteNode(root,pre->data);
30         }
31         else if(root->rchild!=NULL) // 有右子樹,則找后繼來覆蓋root 
32         {
33             node *next = findMin(root->rchild);
34             root->data = next->data;
35             deleteNode(root,next->data);
36         }
37     }
38     else if(root->data > x)  // 根據二叉查找樹特性繼續找x 
39     {
40         deleteNode(root->lchild,x);
41     }
42     else
43     {
44         deleteNode(root->rchild,x);
45     }
46 }

這段代碼還可以進行優化,例如在找到欲刪除結點root的后繼結點next后,不進行遞歸刪除,而通過這樣的手段直接刪除該后繼:假設結點next的父親結點是結點S,顯然next是結點S的左孩子,那么由於結點next一定沒有左子樹,便可以直接把結點next的右子樹代替結點next成為S的左子樹,這樣就刪去了結點next。前驅同理。但這個優化需要在結點定義中額外記錄每個結點的父親結點地址。
而且我們可以發現上面的二叉查找樹刪除操作總是優先刪除前驅(或者后繼),這樣容易使樹的左右子樹高度不平衡,最終使二叉查找樹退化成一條鏈。
有兩種方法解決這個問題:1.每次交替刪除前驅或者后繼 2.記錄每個節點子樹的高度,並且總是優先在高度較高的子樹里刪除結點。(也是平衡二叉樹(AVL樹)的原理)

二叉查找樹的一個性質:因為二叉查找樹本身的特點(左<=根<右),因此對二叉查找樹進行中序遍歷,遍歷的結果是有序的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM