java--二叉樹解析及基本實現


一.二叉樹的結構
  在進行鏈表結構開發的過程之中,會發現所有的數據按照首尾相連的狀態進行保存,那么 在進行數據查詢時為了判斷數據是否存在,這種情況下它所面對的時間復雜度就是"O(n)",如果說它現在的數據量比較小(<30)是不會對性能造成什么影響的,而一旦保存的數據量很大,這個時候時間復雜度就會嚴重損耗程序的運行性能,那么對於數據的存儲結構就必須發生改變,應該盡可能的減少檢索次數為出發點進行設計.對於現在的數據結構而言,最好的性能就是"O(logn)",現在想要實現它,就可以使用二叉樹的結構來完成.
  如果想要實現一顆樹結構的定義,那么就需要去考慮數據的存儲形式,在二叉樹的實現中,基本原理如下:取第一個保存的數據為根節點,當比根節點小或相等的數據需要放在根的左子樹,而大於節點的數據要放在該節點的右子樹.同時,在每一個樹節點中需要保存的東西有如下:父節點,數據,左子樹,右子樹

--當要進行數據檢索時,此時就需要進行每個節點的判斷,例如現在我們要查找數據23,那么我們可以知道23比25小,那么查詢25的左子樹,而25的左子樹為20比數據23小,則查詢他的右子樹,其右子樹23就是我們所需要的數據.其時間復雜度為O(logn).
--對於二叉樹的查詢,也有三種形式,分別為:前序遍歷(根-左-右),中序遍歷(左-根-右),后序遍歷(左-右-根),以中序遍歷為例,則以上的數據在中序遍歷的時候最終的結果就是(10,18,20,23,25,40,50,100),可以發現二叉樹中的內容全部都是排序的結果.

二.二叉樹的基礎實現
  二叉樹實現的關鍵問題在於數據的保存,而且數據由於牽扯到對象比較的問題,那么一定要有比較器的支持,而首選的比較器就是Comparable,以Person數據為例:

 1 package 常用類庫.二叉樹的實現;  2 
 3 import javax.jws.Oneway;  4 import java.lang.reflect.Array;  5 import java.util.Arrays;  6 
 7 /**
 8  * @author : S K Y  9  * @version :0.0.1  10  */
 11 class Person implements Comparable<Person> {  12     private String name;  13     private int age;  14 
 15     public Person() {  16  }  17 
 18     public Person(String name, int age) {  19         this.name = name;  20         this.age = age;  21  }  22 
 23     public String getName() {  24         return name;  25  }  26 
 27     public void setName(String name) {  28         this.name = name;  29  }  30 
 31     public int getAge() {  32         return age;  33  }  34 
 35     public void setAge(int age) {  36         this.age = age;  37  }  38 
 39  @Override  40     public int compareTo(Person o) {  41         return this.age - o.age;  42  }  43 
 44  @Override  45     public String toString() {  46         return "Person{" +
 47                 "name='" + name + '\'' +
 48                 ", age=" + age +
 49                 '}';  50  }  51 }  52 
 53 class BinaryTree<T extends Comparable<T>> {  54     private class Node {  55         private Comparable<T> data;     //存放Comparable,可以比較大小
 56         private Node parent;    //存放父節點
 57         private Node left;      //保存左子樹
 58         private Node right;     //保存右子樹
 59 
 60         public Node(Comparable<T> data) {        //構造方式直接實現數據的存儲
 61             this.data = data;  62  }  63 
 64         /**
 65  * 實現節點數據的適當位置的存儲  66  *  67  * @param newNode 創建的新節點  68          */
 69         void addNode(Node newNode) {  70             if (newNode.data.compareTo((T) this.data) <= 0) {   //比當前的節點小
 71                 if (this.left == null) {      //沒有左子樹,進行保存
 72                     this.left = newNode;  73                     newNode.parent = this;      //保存父節點
 74                 } else {     //需要向左邊繼續判斷
 75                     this.left.addNode(newNode);     //繼續向下判斷
 76  }  77             } else {     //比根節點的數據要大
 78                 if (this.right == null) {     //沒有右子樹
 79                     this.right = newNode;  80                     newNode.parent = this;      //保存父節點
 81                 } else {  82                     this.right.addNode(newNode);        //繼續向下進行
 83  }  84  }  85  }  86 
 87         /**
 88  * 實現所有數據的獲取處理,按照中序遍歷的形式來完成  89          */
 90         void toArrayNode() {  91             if (this.left != null) {  //存在左子樹
 92                 this.left.toArrayNode();        //遞歸調用
 93  }  94             BinaryTree.this.returnData[BinaryTree.this.foot++] = this.data;  95             if (this.right != null) {  96                 this.right.toArrayNode();  97  }  98  }  99 
100  } 101 
102     /*===========以下是二叉樹的功能實現=============*/
103     private Node root;      //保存的根節點
104     private int count;      //保存數據個數
105     private Object[] returnData;        //返回的數據
106     private int foot = 0;       //腳標控制
107 
108     /**
109  * 進行數據的增加 110  * 111  * @param data 需要保存的數據 112  * @throws NullPointerException 保存的數據不允許為空 113      */
114     public void add(Comparable<T> data) { 115         if (data == null) { 116             throw new NullPointerException("保存的數據不允許為空"); 117  } 118         //所有的數據本身不具備有節點關系的匹配,那么一定要將其包裝在Node類之中
119         Node newNode = new Node(data);      //保存節點
120         if (this.root == null) {      //表名此時沒有根節點,那么第一個保存的數據將作為根節點
121             this.root = newNode; 122         } else {     //需要將其保存到一個合適的節點
123             this.root.addNode(newNode); 124  } 125         count++; 126  } 127 
128 
129     /**
130  * 以對象數組的形式返回數據,如果沒有數據則返回null 131  * 132  * @return 全部數據 133      */
134     public Object[] toArray() { 135         if (this.count == 0) return null; 136         this.foot = 0;      //腳標清零
137         this.returnData = new Object[count]; 138         this.root.toArrayNode(); 139         return returnData; 140  } 141 
142 } 143 
144 public class MyBinaryTree { 145     public static void main(String[] args) { 146         BinaryTree<Person> tree = new BinaryTree<>(); 147         tree.add(new Person("小紅", 20)); 148         tree.add(new Person("小光", 80)); 149         tree.add(new Person("小亮", 40)); 150         tree.add(new Person("小龍", 25)); 151         tree.add(new Person("小C", 77)); 152         tree.add(new Person("小D", 66)); 153         tree.add(new Person("小九", 35)); 154         tree.add(new Person("小Q", 54)); 155         Object[] objects = tree.toArray(); 156  System.out.println(Arrays.toString(objects)); 157  } 158 }

--運行結果

[Person{name='小紅', age=20}, Person{name='小龍', age=25}, Person{name='小九', age=35}, Person{name='小亮', age=40}, Person{name='小Q', age=54}, Person{name='小D', age=66}, Person{name='小C', age=77}, Person{name='小光', age=80}] Process finished with exit code 0

--在以上的代碼實現中采用了遞歸算法的操作,采用遞歸算法,相對而言其代碼更加的簡介明了,但是此時在進行數據添加的時候,只是實現了節點關系的保存,而這種關系保存后的結果就是所有的數據都是有序排列的.

三.數據刪除
  二叉樹的數據刪除操作是非常復雜的,因為在進行數據刪除的時候需要考慮的情況是比較多的:
--1.如果刪除的節點沒有子節點,那么直接刪除該節點即可

 


--2.如果待刪除節點只有一個子節點,那么刪除該節點之后,考慮兩種情況的分析:
  a.只有一個左子樹:將其左子樹放置於原來父節點的位置
  b.只有一個右子樹:也是將其右子樹放置於原來父節點的位置
--3.如果刪除節點存在兩個子節點,那么刪除該節點,首先需要找到當前節點的后繼節點,這個后繼節點就是其右子樹的左側葉子節點(及該節點下的最后一個左子樹)


--具體的代碼實現

 1 package 常用類庫.二叉樹的實現;  2 
 3 import java.util.Arrays;  4 
 5 /**
 6  * @author : S K Y  7  * @version :0.0.1  8  */
 9 class Person implements Comparable<Person> {  10     private String name;  11     private int age;  12 
 13     public Person() {  14  }  15 
 16     public Person(String name, int age) {  17         this.name = name;  18         this.age = age;  19  }  20 
 21     public String getName() {  22         return name;  23  }  24 
 25     public void setName(String name) {  26         this.name = name;  27  }  28 
 29     public int getAge() {  30         return age;  31  }  32 
 33     public void setAge(int age) {  34         this.age = age;  35  }  36 
 37  @Override  38     public int compareTo(Person o) {  39         return this.age - o.age;  40  }  41 
 42  @Override  43     public String toString() {  44         return "Person{" +
 45                 "name='" + name + '\'' +
 46                 ", age=" + age +
 47                 '}';  48  }  49 }  50 
 51 class BinaryTree<T extends Comparable<T>> {  52     private class Node {  53         private Comparable<T> data;     //存放Comparable,可以比較大小
 54         private Node parent;    //存放父節點
 55         private Node left;      //保存左子樹
 56         private Node right;     //保存右子樹
 57 
 58         public Node(Comparable<T> data) {        //構造方式直接實現數據的存儲
 59             this.data = data;  60  }  61 
 62         /**
 63  * 實現節點數據的適當位置的存儲  64  *  65  * @param newNode 創建的新節點  66          */
 67         void addNode(Node newNode) {  68             if (newNode.data.compareTo((T) this.data) <= 0) {   //比當前的節點小
 69                 if (this.left == null) {      //沒有左子樹,進行保存
 70                     this.left = newNode;  71                     newNode.parent = this;      //保存父節點
 72                 } else {     //需要向左邊繼續判斷
 73                     this.left.addNode(newNode);     //繼續向下判斷
 74  }  75             } else {     //比根節點的數據要大
 76                 if (this.right == null) {     //沒有右子樹
 77                     this.right = newNode;  78                     newNode.parent = this;      //保存父節點
 79                 } else {  80                     this.right.addNode(newNode);        //繼續向下進行
 81  }  82  }  83  }  84 
 85         /**
 86  * 實現所有數據的獲取處理,按照中序遍歷的形式來完成  87          */
 88         void toArrayNode() {  89             if (this.left != null) {  //存在左子樹
 90                 this.left.toArrayNode();        //遞歸調用
 91  }  92             System.out.println(foot + " " + this.data + " parent:" + this.parent + " left:" + this.left + " right:" + this.right);  93             BinaryTree.this.returnData[BinaryTree.this.foot++] = this.data;  94             if (this.right != null) {  95                 this.right.toArrayNode();  96  }  97  }  98 
 99  @Override 100         public String toString() { 101             return "Node{" +
102                     "data=" + data +
103                     '}'; 104  } 105  } 106 
107     /*===========以下是二叉樹的功能實現=============*/
108     private Node root;      //保存的根節點
109     private int count;      //保存數據個數
110     private Object[] returnData;        //返回的數據
111     private int foot = 0;       //腳標控制
112 
113     /**
114  * 進行數據的增加 115  * 116  * @param data 需要保存的數據 117  * @throws NullPointerException 保存的數據不允許為空 118      */
119     public void add(Comparable<T> data) { 120         if (data == null) { 121             throw new NullPointerException("保存的數據不允許為空"); 122  } 123         //所有的數據本身不具備有節點關系的匹配,那么一定要將其包裝在Node類之中
124         Node newNode = new Node(data);      //保存節點
125         if (this.root == null) {      //表名此時沒有根節點,那么第一個保存的數據將作為根節點
126             this.root = newNode; 127         } else {     //需要將其保存到一個合適的節點
128             this.root.addNode(newNode); 129  } 130         count++; 131  } 132 
133     /**
134  * 返回樹中當前的節點,如果存在 135  * 136  * @param data 所需要在樹中獲取節點的對象 137  * @return 書中的當前節點, 如果不存在, 則返回null 138      */
139     private Node getNode(Comparable<T> data) { 140         Node compareNode = BinaryTree.this.root; //當前比較的Node節點
141         int i;      //當前的比較結果
142         while ((i = data.compareTo((T) compareNode.data)) != 0) { 143             if (i < 0) {    //當前節點比此節點小
144                 compareNode = compareNode.left; 145             } else {         //當前節點比此節點大
146                 compareNode = compareNode.right; 147  } 148             if (compareNode == null) return null;         //不存在此節點,跳出循環,說明未找到數據
149  } 150         return compareNode; 151  } 152 
153     /**
154  * 判斷當前節點是否存在 155  * 156  * @param data 需要判斷的加節點 157  * @return 如果當前節點存在則返回true, 不存在則返回false 158  * @throws NullPointerException 查詢的數據不允許為空 159      */
160     public boolean contains(Comparable<T> data) { 161         if (data == null) return false;      //當前對象為空
162         if (this.count == 0) return false;       //當前不存在數據
163         return getNode(data) != null; 164  } 165 
166 
167     /**
168  * 執行節點的刪除處理 169  * 170  * @param data 需要刪除的節點數據 171      */
172     public void remove(Comparable<T> data) { 173         if (this.contains(data)) {       //要刪除的數據存在 174             //首先需要找到要刪除的節點
175             Node removeNode = this.getNode(data); 176             if (removeNode.left == null && removeNode.right == null) {  //情況1:當前節點不存在子節點 177                 //此時只要斷開該刪除節點的連接即可
178                 if (removeNode.equals(removeNode.parent.left)) { 179                     removeNode.parent.left = null; 180                 } else { 181                     removeNode.parent.right = null; 182  } 183                 removeNode.parent = null;       //斷開刪除節點的引用
184             } else if (removeNode.left == null) {       //此時說明只存在right子樹
185                 if (removeNode.equals(removeNode.parent.left)) { 186                     removeNode.parent.left = removeNode.right; 187                 } else { 188                     removeNode.parent.right = removeNode.right; 189  } 190                 removeNode.right.parent = removeNode.parent; 191                 removeNode.parent = null; 192             } else if (removeNode.right == null) {       //此時說明只存在left子樹
193                 if (removeNode.equals(removeNode.parent.left)) { 194                     removeNode.parent.left = removeNode.left; 195                 } else { 196                     removeNode.parent.right = removeNode.left; 197  } 198                 removeNode.left.parent = removeNode.parent; 199                 removeNode.parent = null; 200             } else {         //兩邊都有節點
201                 Node needMoveNode = removeNode.right;       //所需移動的節點
202                 System.out.println("needMoveNode: " + needMoveNode.data); 203                 while (needMoveNode.left != null) { 204                     needMoveNode = needMoveNode.left; 205                 }       //此時已經獲取刪除節點的最小左節點,需要將其替代原來的節點 206                 //考慮刪除節點的右節點不存在左節點的情況,及刪除節點的右節點就是最終的needMoveNode
207                 if (needMoveNode.equals(needMoveNode.parent.right)) { 208                     needMoveNode.parent.right = needMoveNode.right; 209                 } else { 210                     needMoveNode.parent.left = needMoveNode.right; 211  } 212                 //替換節點的數據內容
213                 removeNode.data = needMoveNode.data; 214                 //斷開needMoveNode的連接
215                 needMoveNode.parent = null; 216 
217  } 218             this.count--; 219  } 220  } 221 
222     /**
223  * 以對象數組的形式返回數據,如果沒有數據則返回null 224  * 225  * @return 全部數據 226      */
227     public Object[] toArray() { 228         if (this.count == 0) return null; 229         this.foot = 0;      //腳標清零
230         System.out.println("count: " + count); 231         this.returnData = new Object[count]; 232         this.root.toArrayNode(); 233         return returnData; 234  } 235 
236 } 237 
238 public class MyBinaryTree { 239     public static void main(String[] args) { 240         //為了驗證算法結構的准確性,將其內容設置為與圖示相同
241         BinaryTree<Person> tree = new BinaryTree<>(); 242         tree.add(new Person("小紅", 25)); 243         tree.add(new Person("小光", 20)); 244         tree.add(new Person("小亮", 40)); 245         tree.add(new Person("小龍", 18)); 246         tree.add(new Person("小C", 23)); 247         tree.add(new Person("小D", 50)); 248         tree.add(new Person("小九", 10)); 249         tree.add(new Person("小Q", 22)); 250         tree.add(new Person("小Q", 24)); 251         tree.add(new Person("小Q", 100)); 252         Object[] objects = tree.toArray(); 253  System.out.println(Arrays.toString(objects)); 254         //刪除23節點
255         System.out.println("=======刪除22節點========"); 256         tree.remove(new Person("小Q", 22)); 257  System.out.println(Arrays.toString(tree.toArray())); 258         System.out.println("=======刪除18節點========"); 259         tree.add(new Person("小Q", 22)); 260         tree.remove(new Person("小龍", 18)); 261  System.out.println(Arrays.toString(tree.toArray())); 262         System.out.println("=======刪除50節點========"); 263         tree.add(new Person("小龍", 18)); 264         tree.remove(new Person("小D", 50)); 265  System.out.println(Arrays.toString(tree.toArray())); 266         System.out.println("=======刪除23節點========"); 267         tree.add(new Person("小D", 50)); 268         tree.remove(new Person("小C", 23)); 269  System.out.println(Arrays.toString(tree.toArray())); 270         System.out.println("=======刪除20節點========"); 271         tree.add(new Person("小C", 23)); 272         tree.remove(new Person("小光", 20)); 273  System.out.println(Arrays.toString(tree.toArray())); 274         System.out.println("=======刪除25根節點========"); 275         tree.add(new Person("小光", 20)); 276         tree.remove(new Person("小紅", 25)); 277  System.out.println(Arrays.toString(tree.toArray())); 278  } 279 }

--可以發現這種樹結構的刪除操作是非常繁瑣的,所以如果不是必須的情況下不建議使用刪除


免責聲明!

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



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