二叉樹的遍歷


1.前序遍歷

前序遍歷(DLR,lchild,data,rchild),是二叉樹遍歷的一種,也叫做先根遍歷、先序遍歷、前序周游,可記做根左右。前序遍歷首先訪問根結點然后遍歷左子樹,最后遍歷右子樹。

前序遍歷首先訪問根結點然后遍歷左子樹,最后遍歷右子樹。在遍歷左、右子樹時,仍然先訪問根結點,然后遍歷左子樹,最后遍歷右子樹。                                                              
若二叉樹為空則結束返回,否則: 
(1)訪問根結點。
(2)前序遍歷左子樹。
(3)前序遍歷右子樹 。前序遍歷                           
需要注意的是:遍歷左右子樹時仍然采用前序遍歷方法。
如圖所示二叉樹

 


前序遍歷結果:ABDECF
已知后序遍歷和中序遍歷,就能確定前序遍歷。
    其實在遍歷二叉樹的時候有三次遍歷, 比如前序遍歷:A->B->D->D(D左子節點並返回到D)->D(D右子節點並返回到D)->B->E->E(左)->E(右)->->B->A->C->F->F(左)->F(右)->C->C(右),所以可以用棧結構,把遍歷到的節點壓進棧,沒子節點時再出棧。也可以用遞歸的方式,遞歸的輸出當前節點,然后遞歸的輸出左子節點,最后遞歸的輸出右子節點。直接看代碼更package test//前序遍歷的遞歸實現與非遞歸實import java.util.Stack;public class Test 

{ public static void main(String[] args) { TreeNode[] node = new TreeNode[10];//以數組形式生成一棵完全二叉樹 for(int i = 0; i < 10; i++) { node[i] = new TreeNode(i); } for(int i = 0; i < 10; i++) { if(i*2+1 < 10) node[i].left = node[i*2+1]; if(i*2+2 < 10) node[i].right = node[i*2+2]; } preOrderRe(node[0]); } public static void preOrderRe(TreeNode biTree) {//遞歸實現  System.out.println(biTree.value); TreeNode leftTree = biTree.left; if(leftTree != null) { preOrderRe(leftTree); } TreeNode rightTree = biTree.right; if(rightTree != null) { preOrderRe(rightTree); } } public static void preOrder(TreeNode biTree) {//非遞歸實現 Stack<TreeNode> stack = new Stack<TreeNode>(); while(biTree != null || !stack.isEmpty()) { while(biTree != null) { System.out.println(biTree.value); stack.push(biTree); 【節點入棧的目的:1.為了左子樹向右子數的轉移】 biTree = biTree.left; } if(!stack.isEmpty()) { biTree = stack.pop(); biTree = biTree.right; } } } }

在非遞歸遍歷的時候,過程是這樣的:A入棧,判斷A的左子樹是否為空,不為空的話,B入棧,同理,D入棧,D的左子樹為空,D的右子樹為空,此時D節點遍歷完成,B的左子樹遍歷完成,
現在開始遍歷B節點的右子樹,F入棧,F左子樹E入棧,F右子樹為空,此時F節點遍歷完成。A的右子樹C入棧,C的左子樹入棧,G的右子樹H入棧,I入棧。此時整個二叉樹遍歷完畢。

class TreeNode//節點結構 { int value; TreeNode left; TreeNode right; TreeNode(int value) { this.value = value; } }

2.中序遍歷

中序遍歷(LDR)是二叉樹遍歷的一種,也叫做中根遍歷、中序周游。在二叉樹中,先左后根再右。巧記:左根右。
中序遍歷首先遍歷左子樹,然后訪問根結點,最后遍歷右子樹
若二叉樹為空則結束返回,
否則:

 
         

(1)中序遍歷左子樹
(2)訪問根結點
(3)中序遍歷右子樹
如右圖所示二叉樹
中序遍歷結果:DBEAFC

import java.util.Stack;
public class Test 
{
    public static void main(String[] args)
    {
        TreeNode[] node = new TreeNode[10];//以數組形式生成一棵完全二叉樹
        for(int i = 0; i < 10; i++)
        {
            node[i] = new TreeNode(i);
        }
        for(int i = 0; i < 10; i++)
        {
            if(i*2+1 < 10)
                node[i].left = node[i*2+1];
            if(i*2+2 < 10)
                node[i].right = node[i*2+2];
        }
        
        midOrderRe(node[0]);
        System.out.println();
        midOrder(node[0]);
    }
    
    public static void midOrderRe(TreeNode biTree)
    {//中序遍歷遞歸實現
        if(biTree == null)
            return;
        else
        {
            midOrderRe(biTree.left);
            System.out.println(biTree.value);
            midOrderRe(biTree.right);
        }
    }
    
    
    public static void midOrder(TreeNode biTree)
    {//中序遍歷費遞歸實現
        Stack<TreeNode> stack = new Stack<TreeNode>();
        while(biTree != null || !stack.isEmpty())
        {
            while(biTree != null)
            {
                stack.push(biTree);
                biTree = biTree.left;
            }
            if(!stack.isEmpty())
            {
                biTree = stack.pop();
                System.out.println(biTree.value);
                biTree = biTree.right;
            }
        }
    }
}
 
class TreeNode//節點結構
{
    int value;
    TreeNode left;
    TreeNode right;
    
    TreeNode(int value)
    {
        this.value = value;
    }
}

3.后序遍歷(難點)后序遍歷(LRD)是二叉樹遍歷的一種,也叫做后根遍歷、后序周游,可記做左右根。后序遍歷有遞歸算法和非遞歸算法兩種。在二叉樹中,先左后右再根。巧記:左右根。后序遍歷首先遍歷左子樹,然后遍歷右子樹,最后訪問根結點,在遍歷左、右子樹時,仍然先遍歷左子樹,然后遍歷右子樹,最后遍歷根結點。即:若二叉樹為空則結束返回,
否則:(1)后序遍歷左子樹
(2)后序遍歷右子樹
(3)訪問根結點
后序遍歷結果:DEBFCA
已知前序遍歷和中序遍歷,就能確定后序遍歷。
算法核心思想:
    首先要搞清楚先序、中序、后序的非遞歸算法共同之處:用棧來保存先前走過的路徑,以便可以在訪問完子樹后,可以利用棧中的信息,回退到當前節點的雙親節點,進行下一步操作。
    后序遍歷的非遞歸算法是三種順序中最復雜的,原因在於,后序遍歷是先訪問左、右子樹,再訪問根節點,而在非遞歸算法中,利用棧回退到時,並不知道是從左子樹回退到根節點,還是從右子樹回退到根節點,如果從左子樹回退到根節點,此時就應該去訪問右子樹,而如果從右子樹回退到根節點,此時就應該訪問根節點。所以相比前序和后序,必須得在壓棧時添加信息,以便在退棧時可以知道是從左子樹返回,還是從右子樹返回進而決定下一步的操作。

import java.util.Stack;
public class Test 
{
    public static void main(String[] args)
    {
        TreeNode[] node = new TreeNode[10];//以數組形式生成一棵完全二叉樹
        for(int i = 0; i < 10; i++)
        {
            node[i] = new TreeNode(i);
        }
        for(int i = 0; i < 10; i++)
        {
            if(i*2+1 < 10)
                node[i].left = node[i*2+1];
            if(i*2+2 < 10)
                node[i].right = node[i*2+2];
        }
        
        postOrderRe(node[0]);
        System.out.println("***");
        postOrder(node[0]);
    }
    
    
    
    public static void postOrderRe(TreeNode biTree)
    {//后序遍歷遞歸實現
        if(biTree == null)
            return;
        else
        {
            postOrderRe(biTree.left);
            postOrderRe(biTree.right);
            System.out.println(biTree.value);
        }
    }
    
    public static void postOrder(TreeNode biTree)
    {//后序遍歷非遞歸實現
        int left = 1;//在輔助棧里表示左節點
        int right = 2;//在輔助棧里表示右節點
        Stack<TreeNode> stack = new Stack<TreeNode>();
        Stack<Integer> stack2 = new Stack<Integer>();//輔助棧,用來判斷子節點返回父節點時處於左節點還是右節點。
        
        while(biTree != null || !stack.empty())
        {
            while(biTree != null)
            {//將節點壓入棧1,並在棧2將節點標記為左節點
                stack.push(biTree);
                stack2.push(left);
                biTree = biTree.left;
            }
            
            while(!stack.empty() && stack2.peek() == right)
            {//如果是從右子節點返回父節點,則任務完成,將兩個棧的棧頂彈出
                stack2.pop();
                System.out.println(stack.pop().value);
            }
            
            if(!stack.empty() && stack2.peek() == left)
            {//如果是從左子節點返回父節點,則將標記改為右子節點
                stack2.pop();
                stack2.push(right);
                biTree = stack.peek().right;
            }
                
        }
    }
}
 
class TreeNode//節點結構
{
    int value;
    TreeNode left;
    TreeNode right;
    
    TreeNode(int value)
    {
        this.value = value;
    }
}
 
 

轉載自:https://blog.csdn.net/coder__666/article/details/80349039

      https://blog.csdn.net/justinzengTM/article/details/80056106(中序遍歷講解的非常詳細)

 

 


-


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM