二叉樹的遍歷和重建
阿新 • • 發佈:2019-02-02
二叉樹是樹的一種,由一個根節點和兩棵互不相交左右子樹的二叉樹組成。
二叉樹有很多性質,就不一一舉例了~
先來說一下最簡單的關於二叉樹常用的3種遍歷方式。層序遍歷就略過了~
前序遍歷:根節點,左節點,右節點。
結果就是:ABDGHCEIF
中序遍歷:左節點,根節點,右節點。
結果就是:GDHBAEICF
後序遍歷:左節點,右節點,根節點。
結果就是:GHDBIEFCA
概念就說到這裡,開始敲程式碼~
首先,建立數節點。
public class TreeNode { private int index; private String data; //資料域 private TreeNode leftChild; //左子樹 private TreeNode rightChild; //右子樹 public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public String getData() { return data; } public void setData(String data) { this.data = data; } public TreeNodeString(String data) { this.data = data; } public TreeNode(int index, String data) { this.index = index; this.data = data; this.leftChild = null; this.rightChild = null; } }
然後我們來構建一個二叉樹:
/** * 構建二叉樹 * A * B C * D E F * G H I */ public void createBinaryTree() { //我們需要一個根節點 root = new TreeNode(1, "A"); TreeNode treeNodeB = new TreeNode(2, "B"); TreeNode treeNodeC = new TreeNode(3, "C"); TreeNode treeNodeD = new TreeNode(4, "D"); TreeNode treeNodeE = new TreeNode(5, "E"); TreeNode treeNodeF = new TreeNode(6, "F"); TreeNode treeNodeG = new TreeNode(7, "G"); TreeNode treeNodeH = new TreeNode(8, "H"); TreeNode treeNodeI = new TreeNode(9, "I"); //分別對應節點的左右子樹。 root.leftChild = treeNodeB; root.rightChild = treeNodeC; treeNodeB.leftChild = treeNodeD; treeNodeD.leftChild = treeNodeG; treeNodeD.rightChild = treeNodeH; treeNodeC.leftChild = treeNodeE; treeNodeC.rightChild = treeNodeF; treeNodeE.rightChild = treeNodeI; }
二叉樹建立完成之後,
開始遍歷,首先使用迴圈的方式進行前序遍歷,迴圈需要藉助棧來實現遍歷。
/** * 前序遍歷 非遞迴 * @param treeNode */ public void nonRecOrder(TreeNode treeNode){ if(treeNode == null){ return; } Stack<TreeNode> stack = new Stack<>(); //首先把根節點放入棧中 stack.push(treeNode); while (!stack.isEmpty()){ //彈出根結點 TreeNode node = stack.pop(); System.out.println("data:" + node.data); //如果有右子結點,則壓入節點 if(node.rightChild!=null){ //如果左節點有值,則右節點在棧底,最後才會輸出 stack.push(node.rightChild); } //如果有右左結點,則壓入節點 if(node.leftChild!=null){ //下次迴圈把左子節點當根節點繼續彈出 stack.push(node.leftChild); } } }
接著來介紹使用遞迴的方式進行遍歷
/**
* 前序遍歷 遞迴
* @param treeNode
*/
public void preOrder(TreeNode treeNode){
if(treeNode == null){
return;
}else{
//首先把每個節點當成根節點輸出
System.out.println("data:" + treeNode.data);
//如果左節點有值,則輸出左節點
preOrder(treeNode.leftChild);
///如果右節點有值,輸出右節點。
preOrder(treeNode.rightChild);
}
}
/**
* 中序遍歷 遞迴
* @param treeNode
*/
public void midOrder(TreeNode treeNode){
if(treeNode == null){
return;
}else{
//遞迴左子樹,直到左節點為null,return。
midOrder(treeNode.leftChild);
//輸出節點。
System.out.println("data:" + treeNode.data);
//遞迴右子樹
midOrder(treeNode.rightChild);
}
}
/**
* 後序遍歷 遞迴
* @param treeNode
*/
public void postOrder(TreeNode treeNode){
if(treeNode == null){
return;
}else{
//遞迴左子樹,直到左節點為null,return。
postOrder(treeNode.leftChild);
//遞迴右子樹,直到左節點為null,return。
postOrder(treeNode.rightChild);
//輸出節點
System.out.println("data:" + treeNode.data);
}
}
二叉樹的遍歷理解了還是比較簡單的,下面來看一下二叉樹的重建。
已知 前序遍歷結果 :ABDGHCEIF 中序遍歷結果:GDHBAEICF,求二叉樹。
根據前序遍歷的特性,可以知道A是根節點,這種在中序中可以得出 A節點左邊的就是左節點,右邊的就是右節點。這個規律可以使用遞迴
下面就來看看重建二叉樹的遞迴程式碼,具體的在程式碼中都有註釋
/**
* 已知前序和中序
* @param pre 前序集合
* @param startPre 前序集合起始點
* @param endPre 前序集合總長度
* @param in 中序集合
* @param startIn 中序集合起始點
* @param endIn 中序集合總長度
* @return
*/
public TreeNodeString resetBinaryTreeString(String[] pre ,int startPre,int endPre,String[] in,int startIn,int endIn){
//如果開始大於結束,就說明這個樹已經結束了
if(startPre > endPre || startIn > endIn) {
return null;
}
//把前序的頭節點放入樹中當根節點
TreeNodeString node = new TreeNodeString(pre[startPre]);
//遍歷中序的所有節點
for (int i = startIn; i <= endIn; i++) {
//找到根節點在中序中的位置
if(pre[startPre] == in[i]){
node.leftChild = resetBinaryTreeString(
pre, //前序集合
startPre + 1, //前序左子樹起始點: startPre + 1
startPre + (i - startIn),//左子樹長度:i是root節點在中序遍歷的位置,
// (i - startIn)是中序遍歷中左子樹的長度,
// 所以左子樹的總長度:前序的 startPre + 中序遍歷中左子樹的長度
in, //中序集合
startIn, //中序左子樹的起始點:startIn
i - 1); //中序左子樹的總長度:i -1
node.rightChild = resetBinaryTreeString(
pre, //前序集合
startPre + (i - startIn) + 1,//前序右子樹起始點: 前序左子樹的長度+1 , {startPre + (i - startIn)} + 1
endPre, //前序右子樹總長度: 前序的總長度
in, //中序集合
i + 1, //中序右子樹的起始點:i+1
endIn); //中序右子樹總長度: 中序的總長度
break;
}
}
return node;
}
這些是最近看二叉樹的一些收穫~簡單的分享一下。
下面貼上全部程式碼:
public class BinaryTree {
private TreeNode root;
public BinaryTree() {
root = new TreeNode(1, "A");
}
/**
* 構建二叉樹
* A
* B C
* D E F
* G H I
*/
public void createBinaryTree() {
root = new TreeNode(1, "A");
TreeNode treeNodeB = new TreeNode(2, "B");
TreeNode treeNodeC = new TreeNode(3, "C");
TreeNode treeNodeD = new TreeNode(4, "D");
TreeNode treeNodeE = new TreeNode(5, "E");
TreeNode treeNodeF = new TreeNode(6, "F");
TreeNode treeNodeG = new TreeNode(7, "G");
TreeNode treeNodeH = new TreeNode(8, "H");
TreeNode treeNodeI = new TreeNode(9, "I");
root.leftChild = treeNodeB;
root.rightChild = treeNodeC;
treeNodeB.leftChild = treeNodeD;
treeNodeD.leftChild = treeNodeG;
treeNodeD.rightChild = treeNodeH;
treeNodeC.leftChild = treeNodeE;
treeNodeC.rightChild = treeNodeF;
treeNodeE.rightChild = treeNodeI;
}
/**
* 前序遍歷 遞迴
* @param treeNode
*/
public void preOrder(TreeNode treeNode){
if(treeNode == null){
return;
}else{
//首先輸出根節點。
System.out.println("data:" + treeNode.data);
//不斷的呼叫自身函式,把左節點輸出。
preOrder(treeNode.leftChild);
//根節點和左節點輸出完成,輸出右節點。
preOrder(treeNode.rightChild);
}
}
/**
* 中序遍歷 遞迴
* @param treeNode
*/
public void midOrder(TreeNode treeNode){
if(treeNode == null){
return;
}else{
midOrder(treeNode.leftChild);
System.out.println("data:" + treeNode.data);
midOrder(treeNode.rightChild);
}
}
/**
* 後序遍歷 遞迴
* @param treeNode
*/
public void postOrder(TreeNode treeNode){
if(treeNode == null){
return;
}else{
postOrder(treeNode.leftChild);
postOrder(treeNode.rightChild);
System.out.println("data:" + treeNode.data);
}
}
/**
* 前序遍歷 非遞迴
* @param treeNode
*/
public void nonRecOrder(TreeNode treeNode){
if(treeNode == null){
return;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(treeNode);
while (!stack.isEmpty()){
//彈出根結點
TreeNode node = stack.pop();
System.out.println("data:" + node.data);
//如果有右子結點,則壓入節點
if(node.rightChild!=null){
//如果左節點有值,則右節點在棧底,最後才會輸出
stack.push(node.rightChild);
}
//如果有右左結點,則壓入節點
if(node.leftChild!=null){
//下次迴圈把左子節點當根節點繼續彈出
stack.push(node.leftChild);
}
}
}
/**
* 已知前序和中序
* @param pre 前序集合
* @param startPre 前序集合起始點
* @param endPre 前序集合總長度
* @param in 中序集合
* @param startIn 中序集合起始點
* @param endIn 中序集合總長度
* @return
*/
public TreeNode resetBinaryTreeString(String[] pre ,int startPre,int endPre,String[] in,int startIn,int endIn){
//如果開始大於結束,就說明這個樹已經結束了
if(startPre > endPre || startIn > endIn) {
return null;
}
//把前序的頭節點放入樹中當根節點
TreeNode node = new TreeNode(pre[startPre]);
//遍歷中序的所有節點
for (int i = startIn; i <= endIn; i++) {
//找到根節點在中序中的位置
if(pre[startIn] == in[i]){
node.leftChild = resetBinaryTreeString(
pre, //前序集合
startPre + 1, //前序左子樹起始點: startPre + 1
startPre + (i - startIn),//左子樹長度:i是root節點在中序遍歷的位置,
// (i - startIn)是中序遍歷中左子樹的長度,
// 所以左子樹的總長度:前序的 startPre + 中序遍歷中左子樹的長度
in, //中序集合
startIn, //中序左子樹的起始點:startIn
i - 1); //中序左子樹的總長度:i -1
node.rightChild = resetBinaryTreeString(
pre, //前序集合
startPre + (i - startIn) + 1,//前序右子樹起始點: 前序左子樹的長度+1 , {startPre + (i - startIn)} + 1
endPre, //前序右子樹總長度: 前序的總長度
in, //中序集合
i + 1, //中序右子樹的起始點:i+1
endIn); //中序右子樹總長度: 中序的總長度
break;
}
}
return node;
}
public static void main(String[] args){
BinaryTree binaryTree= new BinaryTree();
binaryTree.createBinaryTree();
System.out.println("非遞迴前序");
binaryTree.nonRecOrder(binaryTree.root);
System.out.println("遞迴前序");
binaryTree.preOrder(binaryTree.root);
System.out.println("遞迴中序");
binaryTree.midOrder(binaryTree.root);
System.out.println("遞迴後序");
binaryTree.postOrder(binaryTree.root);
String[] pre = {"A","B","D","G","H","C","E","I","F"};
String[] mid = {"G","D","H","B","A","E","I","C","F"};
TreeNode root = binaryTree.resetBinaryTreeString(pre, 0, pre.length - 1, mid, 0, mid.length - 1);
System.out.println("重建二叉樹前序");
binaryTree.preOrder(root);
}
public class TreeNode {
/**
*
*/
private int index;
/**
* 資料域
*/
private String data;
/**
* 左子樹
*/
private TreeNode leftChild;
/**
* 右子樹
*/
private TreeNode rightChild;
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public TreeNode(int index, String data) {
this.index = index;
this.data = data;
this.leftChild = null;
this.rightChild = null;
}
public TreeNode(String data) {
this.data = data;
}
}
}