1. 程式人生 > >二叉樹的遍歷實現遞迴與非遞迴

二叉樹的遍歷實現遞迴與非遞迴

本文實現了二叉樹的深度遍歷演算法,分為遞迴與非遞迴 遞迴的實現非常簡單,基本上沒啥難度 非遞迴的實現需要根據遍歷的順序,將遞迴轉換成迴圈 程式碼中的二叉樹如下 ![遍歷.png](https://upload-images.jianshu.io/upload_images/6264117-62ec04e77355d882.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) --- ### 遞迴 遞迴的實現很簡單,此處不做過多贅述 ``` package cn.lillcol.agst.test; /** * 定義 結點資料結構 */ public class Node { // 資料域 String data = "null"; // 左孩子 Node leftChild; // 右孩子 Node rightChild; // 是否被訪問 boolean isVisit = false; public void setIsVisit(boolean isVisit) { this.isVisit = isVisit; } public boolean getisVisit() { return this.isVisit; } public Node(String data) { this.data = data; } public void setData(String data) { this.data = data; } public void setLeftChild(Node leftChild) { this.leftChild = leftChild; } public void setRightChild(Node rightChild) { this.rightChild = rightChild; } @Override public String toString() { return this.data; } } ``` --- ``` package cn.lillcol.agst.test; /** * 二叉樹遍歷案例遞迴版本 * * @author lillcol * @date 2020-01-16 23:56:51 */ public class BTreeTestRecursion { public static void main(String[] args) { BTreeTestRecursion bTreeTestRecursion = new BTreeTestRecursion(); Node bTree = bTreeTestRecursion.createBTree(); System.out.print("前序遍歷:"); bTreeTestRecursion.preOrderTraverse(bTree); System.out.print("\n中序遍歷:"); bTreeTestRecursion.inOrderTraverse(bTree); System.out.print("\n後序遍歷:"); bTreeTestRecursion.postOrderTraverse(bTree); } /** * 生成一棵樹 * * @return */ public Node createBTree() { Node A = new Node("A"); Node B = new Node("B"); Node C = new Node("C"); Node D = new Node("D"); Node E = new Node("E"); Node F = new Node("F"); Node G = new Node("G"); Node H = new Node("H"); Node I = new Node("I"); A.setLeftChild(B); A.setRightChild(C); B.setLeftChild(D); C.setLeftChild(E); C.setRightChild(F); D.setLeftChild(G); D.setRightChild(H); E.setRightChild(I); return A; } /** * 前序遍歷遞迴版本 * * @param t */ public void preOrderTraverse(Node t) { if (t == null) return; System.out.print(t.data + "->"); preOrderTraverse(t.leftChild); preOrderTraverse(t.rightChild); } /** * 中序遍歷 遞迴版本 * * @param t */ public void inOrderTraverse(Node t) { if (t == null) return; inOrderTraverse(t.leftChild); System.out.print(t.data + "->"); inOrderTraverse(t.rightChild); } /** * 後續遍歷 遞迴版本 * * @param t */ public void postOrderTraverse(Node t) { if (t == null) return; postOrderTraverse(t.leftChild); postOrderTraverse(t.rightChild); System.out.print(t.data + "->"); } } ``` --- ### 非遞迴 非遞迴的實現比起遞迴相對複雜些。 核心是利用棧的特性,記錄訪問過的結點或輸出的結點 非遞迴的實現中,先序遍歷、中序遍歷是比較簡單的,只要按照便利的順序結合程式碼的註釋基本就可以理解了。 比較難的後續遍歷,在實現的過程中發現,如果要按照訪問順序來進行實現,很複雜。 有些實現方式是通過增加一個標誌位標記該借點是否訪問過,但是卻有問題:比如需要考慮很多子樹的情況,判斷情況特別多,只要少一個情況就會出錯。 後面檢視資料還有一個實現的方式相對簡單很多,實現如下: 後序遍歷可以看作逆先序遍歷,此處有兩個關鍵: 1. 結果是先序遍歷的逆序 2. 此處的先序遍歷不是從左到右的先序遍歷,是從右到做的先序遍歷,具體如下圖 ![原理.png](https://upload-images.jianshu.io/upload_images/6264117-618d6b9f001663e7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 下面對比觀察一下結果: ![對比.png](https://upload-images.jianshu.io/upload_images/6264117-0321f2b7b4d52958.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ``` package cn.lillcol.agst.test; import java.util.Stack; /*** * 二叉樹層次遍歷的非遞迴實現版本 * @author lillcol 20200308 */ public class BTreeTestNotRecursion { public static void main(String[] args) throws InterruptedException { BTreeTestNotRecursion bTreeTestNotRecursion = new BTreeTestNotRecursion(); Node bTree = bTreeTestNotRecursion.createBTree(); bTreeTestNotRecursion.inOrderTraverse(bTree); bTreeTestNotRecursion.preOrderTraverse(bTree); bTreeTestNotRecursion.postOrderTraverse(bTree); } /** * 前序遍歷 非遞迴版本 * * @param t */ public void preOrderTraverse(Node t) { if (t == null) return;