一段來自百度百科的對二叉樹的解釋:git
二叉樹節點的聲明:github
static final class Entry<T extends Comparable<T>>{ //保存的數據 private T item; //左子樹 private Entry<T> left; //右子樹 private Entry<T> right; //父節點 private Entry<T> parent; Entry(T item,Entry<T> parent){ this.item = item; this.parent = parent; } }
類屬性:測試
//根節點 private Entry<T> root; //數據量 private int size = 0;
二叉樹添加數據:this
/** * 添加元素 * @param item 待添加元素 * @return 已添加元素 */ public T put(T item){ //每次添加數據的時候都是從根節點向下遍歷 Entry<T> t = root; if (t == null){ //當前的叉樹樹的爲空,將新節點設置爲根節點,父節點爲null root = new Entry<>(item,null);
size++; return root.item; } //天然排序結果,若是傳入的數據小於當前節點返回-1,大於當前節點返回1,不然返回0 int ret = 0; //記錄父節點 Entry<T> p = t; while (t != null){ //與當前節點比較 ret = item.compareTo(t.item); p = t; //插入節點比當前節點小,把當前節點設置爲左子節點,而後與左子比較,以此類推找到合適的位置 if (ret < 0) t = t.left; //大於當前節點 else if (ret > 0) t = t.right; else { //相等就把舊值覆蓋掉 t.item = item; return t.item; } } //建立新節點 Entry<T> e = new Entry<>(item,p); //根據比較結果將新節點放入合適的位置 if (ret < 0) p.left = e; else p.right = e;
size++; return e.item; }
在put的過程當中,使用Comparable<T>中的compareTo來比較兩個元素的大小的,因此在二叉樹中存儲的元素必需要繼承Comparable<T> 類,覆寫compareTo方法。spa
二叉樹刪除數據code
/** * 刪除元素 * 刪除元素若是細分的話,能夠分爲4中:沒有子節點,只有左節點,只有右節點,有兩個子節點 * 1)沒有子節點這種狀況比較簡單,直接刪除就能夠了 * 2)只有左節點或右節點,這兩種狀況處理方式是一致的,只是方向相反,因此在一塊兒講了, * 將刪除節點的父節點的左節點(右節點)指向刪除節點的子節點,將左節點(右節點)指向刪除節點的父節點 * 3)有兩個子節點,這種狀況相對來講比較複雜一點: * 找到刪除節點的前驅節點或後繼節點,而後將前驅或後繼節點的值賦給刪除節點,而後將前驅或後繼節點刪除。本質是刪除前驅或後繼節點 * 前驅節點的特色: * 1)刪除的左子節點沒有右子節點,那麼左子節點即爲前驅節點 * 2)刪除節點的左子節點有右子節點,那麼最右邊的最後一個節點即爲前驅節點 * 後繼節點的特色: * 與前驅節點恰好相反,老是右子節點,或則右子節點的最左子節點;例如:刪除節點爲c ,那麼前驅節點爲 m,後繼節點爲n * a * / \ * b c * / \ / \ * d e f g * / \ / \ / \ / \ * @param item 刪除元素 h i j k l m n o * @return 刪除結果 */ public boolean remove(T item){ //獲取刪除節點 Entry<T> delEntry = getEntry(item); if (delEntry == null) return false; //刪除節點的父節點 Entry<T> p = delEntry.parent; size--; //狀況1:沒有子節點 if (delEntry.left == null && delEntry.right == null){ //刪除節點爲根節點 if (delEntry == root){ root = null; }else {//非根節點 //刪除的是父節點的左節點 if (delEntry == p.left){ p.left = null; }else {//刪除右節點 p.right = null; } } //狀況2:刪除的節點只有左節點 }else if (delEntry.right == null){ Entry<T> lc = delEntry.left; //刪除的節點爲根節點,將刪除節點的左節點設置成根節點 if (p == null) { lc.parent = null; root = lc; } else {//非根節點 if (delEntry == p.left){//刪除左節點 p.left = lc; }else {//刪除右節點 p.right = lc; } lc.parent = p; } //狀況3:刪除節點只有右節點 }else if (delEntry.left == null){ Entry<T> rc = delEntry.right; //刪除根節點 if (p == null) { rc.parent = null; root = rc; }else {//刪除非根節點 if (delEntry == p.left) p.left = rc; else p.right = rc; rc.parent = p; } //狀況4:刪除節點有兩個子節點 }else {//有兩個節點,找到後繼節點,將值賦給刪除節點,而後將後繼節點刪除掉便可 Entry<T> successor = successor(delEntry);//獲取到後繼節點 delEntry.item = successor.item; //後繼節點爲右子節點 if (delEntry.right == successor){ //右子節點有右子節點 if (successor.right != null) { delEntry.right = successor.right; successor.right.parent = delEntry; }else {//右子節點沒有子節點 delEntry.right = null; } }else {//後繼節點一定是左節點 successor.parent.left = null; } return true; } //讓gc回收 delEntry.parent = null; delEntry.left = null; delEntry.right = null; return true; } /** * 獲取節點節點 * @param item 獲取節點 * @return 獲取到的節點,可能爲null */ private Entry<T> getEntry(T item){ Entry<T> t = root; int ret; //從根節點開始遍歷 for (;t != null;){ ret = item.compareTo(t.item); if (ret < 0) t = t.left; else if (ret > 0) t = t.right; else return t; } return null; } /** * 查找後繼節點 * @param delEntry 刪除節點 * @return 後繼節點 */ private Entry<T> successor(Entry<T> delEntry) { Entry<T> r = delEntry.right;//r節點一定不爲空 while (r.left != null){ r = r.left; } return r; }
二叉樹獲取節點orm
/** * 判斷是否存在該元素 * @param item 查找元素 * @return 結果 */ public boolean contains(T item){ return getEntry(item) != null; } /** * 獲取節點節點 * @param item 獲取節點 * @return 獲取到的節點,可能爲null */ private Entry<T> getEntry(T item){ Entry<T> t = root; int ret; //從根節點開始遍歷 for (;t != null;){ ret = item.compareTo(t.item); if (ret < 0) t = t.left; else if (ret > 0) t = t.right; else return t; } return null; }
由於我這個例子是存儲一個元素,獲取到的元素和傳進去的元素是一致的,因此我用contains方法來判斷返回true即表示獲取成功了,不返回獲取到的結果了。固然,若是將entry存儲的元素改成kv形式的話,就可使用get方法了。blog
二叉樹的遍歷排序
二叉樹的遍歷能夠分爲三種:前序遍歷、中序遍歷和後續遍歷,其中中序遍歷是最經常使用的遍歷方式,由於它遍歷出來的結果的升序的。繼承
前序遍歷:
/** * 前序遍歷 * @param e 開始遍歷元素 */ public void prevIterator(Entry<T> e){ if (e != null) { System.out.print(e.item + " "); prevIterator(e.left); prevIterator(e.right); } }
中序遍歷:
/** * 中序遍歷 * @param e */ public void midIterator(Entry<T> e){ if (e != null){ midIterator(e.left); System.out.print(e.item + " "); midIterator(e.right); } }
後序遍歷:
/** * 後續遍歷 * @param e 開始遍歷元素 */ public void subIterator(Entry<T> e){ if (e != null) { subIterator(e.left); subIterator(e.right); System.out.print(e.item + " "); } }
demo完整的代碼:也能夠到個人github中下載代碼:https://github.com/rainple1860/MyCollection
package com.rainple.collections; /** * 二叉樹 * @param <T> */ public class BinaryTree<T extends Comparable<T>> { //根節點 private Entry<T> root; //數據量 private int size = 0; public BinaryTree(){} /** * 添加元素 * @param item 待添加元素 * @return 已添加元素 */ public T put(T item){ //每次添加數據的時候都是從根節點向下遍歷 Entry<T> t = root; size++; if (t == null){ //當前的叉樹樹的爲空,將新節點設置爲根節點,父節點爲null root = new Entry<>(item,null); return root.item; } //天然排序結果,若是傳入的數據小於當前節點返回-1,大於當前節點返回1,不然返回0 int ret = 0; //記錄父節點 Entry<T> p = t; while (t != null){ //與當前節點比較 ret = item.compareTo(t.item); p = t; //插入節點比當前節點小,把當前節點設置爲左子節點,而後與左子比較,以此類推找到合適的位置 if (ret < 0) t = t.left; //大於當前節點 else if (ret > 0) t = t.right; else { //相等就把舊值覆蓋掉 t.item = item; return t.item; } } //建立新節點 Entry<T> e = new Entry<>(item,p); //根據比較結果將新節點放入合適的位置 if (ret < 0) p.left = e; else p.right = e; return e.item; } public void print(){ midIterator(root); } /** * 中序遍歷 * @param e */ public void midIterator(Entry<T> e){ if (e != null){ midIterator(e.left); System.out.print(e.item + " "); midIterator(e.right); } } /** * 獲取根節點 * @return 根節點 */ public Entry<T> getRoot(){return root;} /** * 前序遍歷 * @param e 開始遍歷元素 */ public void prevIterator(Entry<T> e){ if (e != null) { System.out.print(e.item + " "); prevIterator(e.left); prevIterator(e.right); } } /** * 後續遍歷 * @param e 開始遍歷元素 */ public void subIterator(Entry<T> e){ if (e != null) { subIterator(e.left); subIterator(e.right); System.out.print(e.item + " "); } } /** * 獲取節點節點 * @param item 獲取節點 * @return 獲取到的節點,可能爲null */ private Entry<T> getEntry(T item){ Entry<T> t = root; int ret; //從根節點開始遍歷 for (;t != null;){ ret = item.compareTo(t.item); if (ret < 0) t = t.left; else if (ret > 0) t = t.right; else return t; } return null; } /** * 判斷是否存在該元素 * @param item 查找元素 * @return 結果 */ public boolean contains(T item){ return getEntry(item) != null; } /** * 刪除元素 * 刪除元素若是細分的話,能夠分爲4中:沒有子節點,只有左節點,只有右節點,有兩個子節點 * 1)沒有子節點這種狀況比較簡單,直接刪除就能夠了 * 2)只有左節點或右節點,這兩種狀況處理方式是一致的,只是方向相反,因此在一塊兒講了, * 將刪除節點的父節點的左節點(右節點)指向刪除節點的子節點,將左節點(右節點)指向刪除節點的父節點 * 3)有兩個子節點,這種狀況相對來講比較複雜一點: * 找到刪除節點的前驅節點或後繼節點,而後將前驅或後繼節點的值賦給刪除節點,而後將前驅或後繼節點刪除。本質是刪除前驅或後繼節點 * 前驅節點的特色: * 1)刪除的左子節點沒有右子節點,那麼左子節點即爲前驅節點 * 2)刪除節點的左子節點有右子節點,那麼最右邊的最後一個節點即爲前驅節點 * 後繼節點的特色: * 與前驅節點恰好相反,老是右子節點,或則右子節點的最左子節點;例如:刪除節點爲c ,那麼前驅節點爲 m,後繼節點爲n * a * / \ * b c * / \ / \ * d e f g * / \ / \ / \ / \ * @param item 刪除元素 h i j k l m n o * @return 刪除結果 */ public boolean remove(T item){ //獲取刪除節點 Entry<T> delEntry = getEntry(item); if (delEntry == null) return false; //刪除節點的父節點 Entry<T> p = delEntry.parent; size--; //狀況1:沒有子節點 if (delEntry.left == null && delEntry.right == null){ //刪除節點爲根節點 if (delEntry == root){ root = null; }else {//非根節點 //刪除的是父節點的左節點 if (delEntry == p.left){ p.left = null; }else {//刪除右節點 p.right = null; } } //狀況2:刪除的節點只有左節點 }else if (delEntry.right == null){ Entry<T> lc = delEntry.left; //刪除的節點爲根節點,將刪除節點的左節點設置成根節點 if (p == null) { lc.parent = null; root = lc; } else {//非根節點 if (delEntry == p.left){//刪除左節點 p.left = lc; }else {//刪除右節點 p.right = lc; } lc.parent = p; } //狀況3:刪除節點只有右節點 }else if (delEntry.left == null){ Entry<T> rc = delEntry.right; //刪除根節點 if (p == null) { rc.parent = null; root = rc; }else {//刪除非根節點 if (delEntry == p.left) p.left = rc; else p.right = rc; rc.parent = p; } //狀況4:刪除節點有兩個子節點 }else {//有兩個節點,找到後繼節點,將值賦給刪除節點,而後將後繼節點刪除掉便可 Entry<T> successor = successor(delEntry);//獲取到後繼節點 delEntry.item = successor.item; //後繼節點爲右子節點 if (delEntry.right == successor){ //右子節點有右子節點 if (successor.right != null) { delEntry.right = successor.right; successor.right.parent = delEntry; }else {//右子節點沒有子節點 delEntry.right = null; } }else {//後繼節點一定是左節點 successor.parent.left = null; } return true; } //讓gc回收 delEntry.parent = null; delEntry.left = null; delEntry.right = null; return true; } /** * 查找後繼節點 * @param delEntry 刪除節點 * @return 後繼節點 */ private Entry<T> successor(Entry<T> delEntry) { Entry<T> r = delEntry.right;//r節點一定不爲空 while (r.left != null){ r = r.left; } return r; } public int size(){return size;} public boolean isEmpty(){return size == 0;} public void clear(){ clear(getRoot()); root = null; } private void clear(Entry<T> e){ if (e != null){ clear(e.left); e.left = null; clear(e.right); e.right = null; } } static final class Entry<T extends Comparable<T>>{ //保存的數據 private T item; //左子樹 private Entry<T> left; //右子樹 private Entry<T> right; //父節點 private Entry<T> parent; Entry(T item,Entry<T> parent){ this.item = item; this.parent = parent; } } }
測試代碼示例:
public static void main(String[] args) { BinaryTree<Integer> binaryTree = new BinaryTree<>(); //放數據 binaryTree.put(73); binaryTree.put(22); binaryTree.put(532); binaryTree.put(62); binaryTree.put(72); binaryTree.put(243); binaryTree.put(42); binaryTree.put(3); binaryTree.put(12); binaryTree.put(52); System.out.println("size: " + binaryTree.size()); binaryTree.put(52); System.out.println("添加相同元素後的size: " + binaryTree.size()); //判斷數據是否存在 System.out.println("數據是否存在:" + binaryTree.contains(12)); //中序遍歷 System.out.print("中序遍歷結果: "); binaryTree.midIterator(binaryTree.getRoot()); System.out.println(); //前序遍歷 System.out.print("前遍歷結果: "); binaryTree.prevIterator(binaryTree.getRoot()); System.out.println(); //後序遍歷 System.out.print("後續遍歷結果: "); binaryTree.subIterator(binaryTree.getRoot()); //刪除數據 System.out.println(); binaryTree.remove(62); System.out.println("刪除數據後判斷是否存在:" + binaryTree.contains(62)); //清空二叉樹 binaryTree.clear(); System.out.print("清空數據後中序遍歷: "); binaryTree.midIterator(binaryTree.getRoot()); }
測試結果:
size: 10 添加相同元素後的size: 10 數據是否存在:true 中序遍歷結果: 3 12 22 42 52 62 72 73 243 532 前遍歷結果: 73 22 3 12 62 42 52 72 532 243 後續遍歷結果: 12 3 52 42 72 62 22 243 532 73 刪除數據後判斷是否存在:false 清空數據後中序遍歷:
純手寫的demo,有什麼錯誤的地方歡迎指正,謝謝你們的閱讀!!!