java語言實現二叉樹
一、概念
對於大量的輸入資料,連結串列的線性訪問時間太慢,不宜使用。樹這種結構極大的縮短了資料的訪問時間。什麼是樹呢?它是怎麼做到提高訪問效率的呢?樹可以用幾種方式定義,定義樹的一種自然方式是遞迴方式,一棵樹是一些節點的集合,這些集合可以是空集;若不是空集,則樹由根(root)節點以及n(n>=0)個子(child)節點組成,這些子節點每一個都有來自根節點的一條有向邊(edge)所連線。
如上圖,這是一棵樹。【A】為根節點,【B】、【C】、【D】、【E】、【F】、【G】為【A】的子(child)節點,另外每個節點下面都可能有任意多個child,也有可能沒有child,對於沒有child 的節點,我們稱為葉(leaf)節點。從根節點到任意節點經過的邊(edge)數稱為該節點的深度。上圖中【E】的深度是1,【P】的深度是3。一棵樹的深度等於該樹最深葉節點的深度。
二、應用
樹的應用有很多,流行的用法之一是目錄結構,如圖是UNIX檔案系統中一個典型的目錄。
這個目錄的根是/usr(* 號表示此為目錄),/usr下面有三個子目錄:mark、alex、bill,alex目錄下面有一個junk的檔案。上圖的所有檔案都可以通過根節點遞迴遍歷得到,如下虛擬碼
private void listAll(int depth){
printName(depth); // 列印出該深度下的節點名稱
if(isDirectory()){
// 如果該節點是資料夾,則繼續遍歷該資料夾下檔案
for(File c:thisDiretory){
listAll(depth+1);
}
}
private void listAll(){
listAll(0);
}
通過這種listAll的遞迴,就可以列印出根節點下的所有節點。
三、二叉樹
二叉樹是一種特殊的樹,其每個節點下最多有兩個子節點。如下圖顯示的是普通樹和二叉樹
上圖普通樹的節點【6】下面有三個子節點,所以它並不屬於二叉樹。
二叉樹有許多和搜尋無關的應用,其主要用處之一就是在編譯器的設計領域。例:表示式樹
上圖是一個算術運算的表示式,我們可以通過遞迴計算左子樹和右子樹所得到的值,應用在根處的運算子算出整棵樹的值。其左子樹的值是a+(b*c),右子樹的值是((d*e)+f)*g,因此整棵樹表示:(a+(b*c))+(((d*e)+f)*g)
四、二叉查詢樹
二叉樹的一個重要的應用是它們在查詢中的使用。於是便衍生出二叉查詢樹這種結構。使二叉樹成為二叉查詢樹的性質是:對於樹中的任意節點X,它的左子樹中所有節點的值都要小於X 的值,而它的右子樹中所有節點的值都要大於X 的值
上圖所示,左邊的為二叉查詢樹,右邊的就不是,因為它不滿足二叉查詢樹的性質。下面我們用程式碼實現二叉查詢樹,並對其進行資料插入和資料刪除的操作。
程式碼實現思路分析:首先定義樹的節點,二叉樹只有一個儲存的值和兩個子節點,所以我們定義的節點物件如下:
private static class BinaryNode<T> {
// 節點中儲存的資料
T element;
// 左子樹
BinaryNode<T> left;
// 右子樹
BinaryNode<T> right;
BinaryNode(T element) {
this(element, null, null);
}
BinaryNode(T element, BinaryNode<T> left, BinaryNode<T> right) {
this.element = element;
this.left = left;
this.right = right;
}
@Override
public String toString() {
return "BinaryNode{" +
"element=" + element +
", left=" + left +
", right=" + right +
'}';
}
}
然後建立二叉查詢樹物件(BinarySearchTree),為了要寫出一個一般的類,我們讓這個類繼承Comparable類,然後定義一個根節點root,新增無參構造器。
public class BinarySearchTree<T extends Comparable<? super T>> {
// 根節點
private BinaryNode<T> root;
BinarySearchTree() {
root = null;
}
}
構造器和根節點定義好,接下來我們就開始新增新增(insert)方法
private void insert(T t){
root = insert(t, root);
}
private BinaryNode<T> insert(T t,BinaryNode<T> n){
if(n == null)
return new BinaryNode(t, null, null);
// 比較插入資料和n節點的元素大小
int compareResult = x.compareTo(n.element);
if(compareResult < 0) // 插入到左子樹中
t.left = insert(t, n.left);
else if(compareResult > 0) // 插入到右子樹中
t.right = insert(t, n.right);
return t;
}
insert方法新增成功以後,我們開始新增刪除(remove)方法,刪除的業務相對比新增要複雜一點,下面我們獻上程式碼:
private void remove(T t){
root = remove(t,BinaryNode root);
}
private BinaryNode<T> remove(T t,BinaryNode<T> n){
if(n == null)
return n;
int compareResult = t.compareTo(n.element);
if(compareResult < 0)
t.left = remove(t, n.left);
else if(compareResult > 0)
t.right = remove(t.right);
else if(t.left !=null && t.right != null){
t.element = findMin(t.right).element;
t.right = remove(t.element, t.right);
}else{
t = ( t.left == null ) ? t.right : t.left
}
}
private BinaryNode<T> findMin(BinaryNode<T> n){
if(n == null)
return null;
else if(n.left == null)
return n
return findMin(n.left);
}
以上,我們已經實現了一棵二叉查詢樹,下面我們對程式碼進行測試
/**
* 二叉查詢樹測試
*/
@Test
public void BinarySearchTreeTest() {
BinarySearchTree<Integer> bt = new BinarySearchTree<>();
bt.insert(6);
bt.insert(2);
bt.insert(8);
bt.insert(1);
bt.insert(4);
bt.insert(3);
bt.printTree();
bt.remove(2);
bt.printTree();
}
該測試新增和刪除之後的資料如下圖顯示
從圖上我們可以看出,新增和刪除符合我們的預期,至此,我們的二叉查詢樹已經實現。
五、存在的缺陷
從二叉樹的定義我們可以知道,每個節點下的子節點最多有兩個,那麼考慮極端情況:每個節點下都只有一個子節點。
我們通過剛剛完成的程式碼進行測試
/**
* 極端情況下二叉查詢樹測試
*/
@Test
public void BinarySearchTreeTest1() {
BinarySearchTree<String> bt = new BinarySearchTree<>();
bt.insert("A");
bt.insert("B");
bt.insert("C");
bt.insert("D");
bt.insert("E");
bt.printTree();
}
以上程式碼執行的結果會產生如下圖的“樹”
從圖上我們可以看出,二叉樹退化了,退化成了一個連結串列,而我們期望的對二叉樹的操作平均耗時在O(log N) ,但這種極端情況下操作耗時為O(N)。
為了避免這種極端情況的效能損耗,我們必須要對這樣的二叉樹結構進行改進。
本章篇幅有限,我們下章再介紹如何改進才能有效的避免這種極端情況的發生,感興趣的讀者朋友可以 關注本公眾號,和我們一起學習探究。
本人因所學有限,如有錯誤之處,望請各位指正!
相關文章
- 平衡二叉樹(AVL樹)和 二叉排序樹轉化為平衡二叉樹 及C語言實現二叉樹排序C語言
- C語言 遞迴實現二叉排序樹的插入C語言遞迴排序
- Java實現紅黑樹(平衡二叉樹)Java二叉樹
- 二叉樹的構建以及遍歷(Java語言描述)二叉樹Java
- 最優二叉樹(哈夫曼樹)Java實現二叉樹Java
- 二叉樹實現二叉樹
- 如何在 Java 中實現二叉搜尋樹Java
- 二叉搜尋樹(Binary Search Tree)(Java實現)Java
- java實現-資料結構之二叉樹(三):線索化二叉樹Java資料結構二叉樹
- Java實現二叉搜尋樹的插入、刪除Java
- js實現完全排序二叉樹、二叉搜尋樹JS排序二叉樹
- 霍夫曼樹(最優二叉樹)的實現二叉樹
- Go語言實現的Java Stream APIGoJavaAPI
- 資料結構之二叉搜尋樹—Java實現資料結構Java
- 144. 二叉樹的前序遍歷(java實現)--LeetCode二叉樹JavaLeetCode
- JavaScript 二叉搜尋樹以及實現翻轉二叉樹JavaScript二叉樹
- Java二叉樹Java二叉樹
- java實現樹Java
- iOS實現反轉二叉樹iOS二叉樹
- 二叉樹的遍歷實現二叉樹
- javascript實現二叉搜尋樹JavaScript
- python實現非平衡二叉樹Python二叉樹
- 如何在Java中實現二叉搜尋樹( binary search tree)?Java
- 二叉查詢樹(查詢、插入、刪除)——C語言C語言
- 使用 Rust 語言編寫 Java JNI 實現RustJava
- 二叉樹 & 二叉查詢樹 ADT [資料結構與演算法分析 c 語言描述]二叉樹資料結構演算法
- 二叉樹 & 二叉查詢樹 ADT【資料結構與演算法分析 c 語言描述】二叉樹資料結構演算法
- Java 樹結構實際應用 四(平衡二叉樹/AVL樹)Java二叉樹
- 實現二叉搜尋樹的新增,查詢和刪除(JAVA)Java
- 二叉搜尋樹的python實現Python
- 使用javascript實現排序二叉樹(2)JavaScript排序二叉樹
- 使用javascript實現排序二叉樹(1)JavaScript排序二叉樹
- 二叉查詢樹概念及實現
- 詳細二叉樹實現c++二叉樹C++
- 劍指offer(java實現)第4題“重建二叉樹”-牛客網Java二叉樹
- 二叉樹遍歷 -- JAVA二叉樹Java
- 二叉樹實現按層 s型列印二叉樹
- Python實現二叉樹的增、刪、查Python二叉樹