【算法與數據結構】二叉搜索樹的Java實現

  爲了更加深刻了解二叉搜索樹,博主本身用Java寫了個二叉搜索樹,有興趣的同窗能夠一塊兒探討探討。node

  首先,二叉搜索樹是啥?它有什麼用呢?算法

  二叉搜索樹, 也稱二叉排序樹,它的每一個節點的數據結構爲1個父節點指針,1個左孩子指針,1個有孩子指針,還有就是本身的數據部分了,由於只有左右兩孩子,因此才叫二叉樹,在此基礎上,該二叉樹還知足另一個條件:每一個結點的左孩子都不大於該結點&&每一個結點的右孩子都大於該結點。這樣,咱們隊這棵樹進行中序遍歷,就能把key從小到大排序了……數據結構

  那麼問題來了,我都有線性表有鏈表了,我還要它幹啥?兩個字!效率ide

  相比線性表,你要搜索一個key,就要執行一次線性時間,算法複雜度爲O(n);而用二叉搜索樹,算法效率是O(lgn)!這是很誘人的數字。下面我用Java實現如下二叉搜索樹,你天然就明白爲何算法複雜度是O(lgn)了。post

  其次,寫一個數據結構,天然而然也要實現對這個數據結構的增、刪、查、改了。測試

  下面是個人思路:ui

 

  1. 建立樹:我是經過一個一個結點的插入來創建一棵二叉搜索樹。
  2. 搜索結點:從根節點開始,進行key的比較,小了就往左走,大了就往右走,最後到了葉子結點都尚未的話,那麼該樹就不存在要搜索的結點了。
  3. 修改結點:修改其實就是查詢,在查詢以後把結點的數據部分給改了而已,這裏我就不重複去實現了。
  4. 刪除結點:這個應該就是最難的了,因此我有必要詳細講,先上圖(很差意思,懶得用軟件畫圖了,將就將就下哈):
當咱們要刪除一個結點時,分以下幾種狀況:
  • 此結點是葉子結點,這個最簡單啦,直接把結點給釋放掉就好了。(如圖刪除9)
  • 此結點只有左孩子,這個也簡單啦,直接把左子樹替換過來就好了。(如圖刪除3)
  • 此結點只有右孩子,同上。(如圖刪除8)
  • 此結點有左右孩子,當出現這種狀況時(如圖刪除7),咱們就要找出該結點的後繼結點(由於右子樹確定存在,因此找確定在右子樹中),而後把這個後繼結點替換到要刪除的結點中,而後繼續執行對這個後繼結點的刪除操做(遞歸刪除操做就好了)。
 
  發現沒?如今個人解題思路是自頂向下去分析…… 自頂向下,逐級求精是一個很偉大的思想!
 
  如今問題來了! 後繼結點怎麼求?咱們來分析一下,當求一個結點的後繼結點時,分爲如下兩種狀況:
  • 當該結點有右孩子時,後繼結點就在右子樹中,就是該右子樹的最小結點
  • 當該結點沒有右孩子時,那後繼結點就知足這個條件:該後繼結點是該結點的祖先&&該結點位於該結點的左子樹中(如圖中的9的後繼結點是12)
  哎呀呀!問題又來了! 最小結點咋辦!很簡單!
  當求一棵樹的最小結點時,那麼就要從這顆樹的根節點開始,一直往左子樹走,就能找到它的最小結點了!
  好了,如今問題逐步解決了!刪除結點的功能也就完成了!
  最後, 沒代碼說個錘子,咱上代碼!
 
首先,寫個測試類:
 1 public class Test {
 2     public static void main(String[] args) {
 3         int[] datas={12,4,5,7,4,8,3,2,6,9};
 4         BinTree tree=new BinTree(datas);
 5         tree.preOrderTraverse();//先序遍歷
 6         tree.midOrderTraverse();//中序遍歷
 7         tree.postOrderTraverse();//後序遍歷
 8         tree.insert(15);    //插入結點
 9         tree.search(7);        //查詢結點
10         tree.search(100);    //查詢一個不存在的結點
11         tree.getMax();        //獲取最大值
12         tree.getMin();        //獲取最小值
13         tree.getPre(7);        //前驅結點
14         tree.getPre(2);        //最前的前驅結點
15         tree.getPost(7);    //後繼結點
16         tree.getPost(15);    //最後的後繼結點
17         tree.delete(5);        //刪除結點
18         tree.delete(0);        //刪除一個不存在的結點
19     }
20 }
View Code

 




而後,二叉搜索樹:

 

  1 public class BinTree {
  2     Node root=null;
  3     private class Node{
  4         Node parent=null;
  5         Node leftChild=null;
  6         Node rightChild=null;
  7         int key;
  8         public Node(int data) {
  9             this.key=data;
 10         }
 11     }
 12     public BinTree(int[] datas) {
 13         buildTree(datas);
 14     }
 15     private void buildTree(int[] datas) {
 16         for (int i = 0; i < datas.length; i++) {
 17             Node node=new Node(datas[i]);
 18             insertNode(node);
 19         }
 20     }
 21     private void insertNode(Node node) {    //插入結點
 22         Node next=this.root;    
 23         Node cur=null;    //用來保存當前結點
 24         while(next!=null){    //當到達葉子結點時,確認位置!
 25             cur=next;
 26             if(node.key>=cur.key){
 27                 next=next.rightChild;
 28             }else{
 29                 next=next.leftChild;
 30             }
 31         }
 32         node.parent=cur;    //插入該結點!
 33         if(cur==null){
 34             this.root=node;  //該樹爲空樹,因此這個是根節點
 35         }else if(node.key>=cur.key){
 36             cur.rightChild=node;
 37         }else{
 38             cur.leftChild=node;
 39         }
 40     }
 41     /*
 42      * 插入一個數
 43      */
 44     public void insert(int data){    
 45         Node node=new Node(data);
 46         System.out.println("插入結點:"+data);
 47         insertNode(node);
 48         this.midOrderTraverse();
 49     }
 50     
 51     /*
 52      * 先序遍歷
 53      */
 54     public void preOrderTraverse(){    
 55         System.out.println("先序遍歷:");
 56         preOrderTraverse(root);
 57         System.out.println();
 58     }
 59     private void preOrderTraverse(Node node){    //先序遍歷
 60         if(node!=null){
 61             System.out.print("-"+node.key+"-");
 62             preOrderTraverse(node.leftChild);
 63             preOrderTraverse(node.rightChild);
 64         }
 65     }
 66     /*
 67      * 中序遍歷
 68      */
 69     public void midOrderTraverse(){    
 70         System.out.println("中序遍歷:");
 71         midOrderTraverse(root);
 72         System.out.println();
 73     }
 74     private void midOrderTraverse(Node node){    //中序遍歷
 75         if(node!=null){
 76             midOrderTraverse(node.leftChild);
 77             System.out.print("-"+node.key+"-");
 78             midOrderTraverse(node.rightChild);
 79         }
 80         
 81     }
 82     
 83     /*
 84      * 後序遍歷
 85      */
 86     public void postOrderTraverse(){
 87         System.out.println("後序遍歷:");
 88         postOrderTraverse(root);
 89         System.out.println();
 90     }
 91     private void postOrderTraverse(Node node){     //後序遍歷
 92         if(node!=null){
 93             System.out.print("-"+node.key+"-");
 94             postOrderTraverse(node.leftChild);
 95             postOrderTraverse(node.rightChild);
 96         }
 97     }
 98     
 99     /*
100      * 搜索結點
101      */
102     public void search(int data){    
103         System.out.println("您要查找的是:"+data);
104         Node node;
105         if((node=searchNode(new Node(data)))==null){
106             System.out.println("樹中沒有該結點!");
107         }else{
108             System.out.println("查找"+node.key+"成功!");
109         }
110     }
111     
112     private Node searchNode(Node node){    //private供內部調用,搜索結點
113         if(node==null){
114             System.out.println("輸入爲空,查找失敗!");
115         }else{
116             if(root==null){
117                 System.out.println("該樹爲空樹!");
118             }else{                        //開始查找
119                 boolean isFound=false;    
120                 Node x=root;
121                 Node y=null;
122                 while(!isFound&&x!=null){    //當查到或者到了葉子節點還沒查到時,終結!
123                     y=x;
124                     if(node.key==x.key){    
125                         isFound=true;
126                     }else{                    //經過比較大小往下面查找
127                         if(node.key>x.key){    
128                             x=x.rightChild;
129                         }else{
130                             x=x.leftChild;
131                         }
132                     }
133                 }
134                 if(isFound){    //沒找到的話,在最後返回null
135                     return y;
136                 }
137             }
138         }
139         return null;
140     }
141     
142     /*
143      * 獲取最大值
144      */
145     public void  getMax(){    
146         Node node;
147         if((node=getMaxNode(root))==null){
148             System.out.println("該樹爲空!");
149         }else{
150             System.out.println("最大的結點是:"+node.key);
151         }
152         
153     }
154     
155     private Node getMaxNode(Node node){    //獲取最大值
156         if(node!=null){
157             Node x=node;
158             Node y=null;
159             while(x!=null){    //一直往右遍歷直到底就是最大值了!
160                 y=x;
161                 x=x.rightChild;
162             }
163             return y;
164         }
165         return null;
166     }
167     
168     /*
169      * 獲取最小值
170      */
171     public void getMin(){    
172         Node node;
173         if((node=getMinNode(root))==null){
174             System.out.println("該樹爲空!");
175         }else{
176             System.out.println("最小的結點是:"+node.key);
177         }
178     }
179     private Node getMinNode(Node node){    //獲取最小值
180         if(node!=null){
181             Node x=node;
182             Node y=null;
183             while(x!=null){    //一直往左遍歷直到底就是最小值了!
184                 y=x;
185                 x=x.leftChild;
186             }
187             return y;
188         }
189         return null;
190     }
191     
192     /*
193      * 獲取前驅結點
194      */
195     public void getPre(int data){    
196         Node node=null;
197         System.out.println(data+"的前驅結點:");
198         if((node=getPreNode(searchNode(new Node(data))))==null){
199             System.out.println("該結點不存在或無前驅結點!");
200         }else{
201             System.out.println(data+"的前驅結點爲:"+node.key);
202         }
203     }
204     
205     private Node getPreNode(Node node){    //獲取前驅結點
206         if(node==null){
207             return null;
208         }
209         if(node.leftChild!=null){    //當有左孩子時,前驅結點就是左子樹的最大值
210             return getMaxNode(node.leftChild);
211         }else{//當不存在左孩子時,前驅結點就是——它的祖先,並且,它在這個祖先的右子樹中。這句話本身畫圖就能理解了
212             Node x=node;
213             Node y=node.parent;
214             while(y!=null&&x==y.leftChild){
215                 x=y;
216                 y=y.parent;
217             }
218             return y;
219         }
220     }
221     
222     /*
223      * 獲取後繼結點
224      */
225     public void getPost(int data){    
226         Node node=null;
227         System.out.println(data+"的後繼結點:");
228         if((node=getPostNode(searchNode(new Node(data))))==null){
229             System.out.println("該結點不存在或無後繼結點!");
230         }else{
231             System.out.println(data+"的後繼結點爲:"+node.key);
232         }
233     }
234     
235     private Node getPostNode(Node node){    //獲取後繼結點
236         if(node==null){
237             return null;
238         }
239         if(node.rightChild!=null){    //當有右孩子時,前驅結點就是右子樹的最小值
240             return getMinNode(node.rightChild);
241         }else{//當不存在右孩子時,後繼結點就是——它的祖先,並且,它在這個祖先的左子樹中。這句話本身畫圖就能理解了
242             Node x=node;
243             Node y=node.parent;
244             while(y!=null&&x==y.rightChild){
245                 x=y;
246                 y=y.parent;
247             }
248             return y;
249         }
250     }
251     
252     
253     /*
254      * 刪除結點
255      */
256     public void delete(int data){    
257         Node node;
258         if((node=searchNode(new Node(data)))==null){//注意!這裏不能new結點!你必須從樹中找該結點!new就是初始化了
259             System.out.println("二叉樹中不存在此結點!");
260             return;
261         }
262         deleteNode(node);
263         System.out.println("刪除結點"+data+"後:");
264         this.midOrderTraverse();
265     }
266     
267     
268     private void deleteNode(Node node){
269         if(node==null){
270             System.out.println("刪除結點不能爲空!");
271             return;
272         }
273         replacedNode(node);
274     }
275     
276     private void replacedNode(Node node) {    //替換結點
277         if(node.leftChild!=null
278                 &&node.rightChild!=null){    //當有左右孩子時,用後繼結點替換
279             replacedNodeOfPost(node);
280         }
281         else
282         {
283             if(node.leftChild!=null){    //當只有左孩子時,直接用左子樹替換
284                 node=node.leftChild;
285             }else if(node.rightChild!=null){    //只有右孩子時,直接有子樹替換
286                 node=node.rightChild;
287             }else{            //當沒有左右孩子時,就直接釋放了這個結點
288                 freeNode(node);
289             }
290         }
291     }
292     
293     
294     private void freeNode(Node node) {    //釋放該結點,斷掉其與父結點的連接
295         if(node==node.parent.leftChild){
296             node.parent.leftChild=null;
297         }else{
298             node.parent.rightChild=null;
299         }
300     }
301     
302     private void replacedNodeOfPost(Node node) {    
303         Node y=this.getPostNode(node);    //找後繼結點
304         node.key=y.key;
305         replacedNode(y);    //替換了key以後,再一次遞歸把如今這個結點給替換了!
306     }
307     
308 }

 


最後是測試結果:
------------------分割線-------------------------
先序遍歷: -12--4--3--2--5--4--7--6--8--9- 中序遍歷: -2--3--4--4--5--6--7--8--9--12- 後序遍歷: -12--4--3--2--5--4--7--6--8--9- 插入結點:15 中序遍歷: -2--3--4--4--5--6--7--8--9--12--15- 您要查找的是:7 查找7成功! 您要查找的是:100 樹中沒有該結點! 最大的結點是:15 最小的結點是:2 7的前驅結點: 7的前驅結點爲:6 2的前驅結點: 該結點不存在或無前驅結點! 7的後繼結點: 7的後繼結點爲:8 15的後繼結點: 該結點不存在或無後繼結點! 刪除結點5後: 中序遍歷: -2--3--4--4--6--7--8--9--12--15- 二叉樹中不存在此結點!
相關文章
相關標籤/搜索