二叉查找樹的特點:
在二叉查找樹中左子樹上所有結點的數據都小於等於根結點的數據,而右子樹上所有結點的數據都大於根結點的數據
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樹)的原理)
二叉查找樹的一個性質:因為二叉查找樹本身的特點(左<=根<右),因此對二叉查找樹進行中序遍歷,遍歷的結果是有序的。