二叉樹的遍歷實現遞迴與非遞迴
阿新 • • 發佈:2020-03-09
本文實現了二叉樹的深度遍歷演算法,分為遞迴與非遞迴
遞迴的實現非常簡單,基本上沒啥難度
非遞迴的實現需要根據遍歷的順序,將遞迴轉換成迴圈
程式碼中的二叉樹如下
![遍歷.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;