數據結構--樹的非遞歸遍歷


    樹的遞歸遍歷代碼非常簡單易懂,但是由於遞歸會占用非常多的棧空間,因此非遞歸的遍歷樹也是必須要掌握的。因此最近仔細分析了很多的代碼以及理解了遍歷的過程,最后敲一遍並在這里記錄一下,以后可以快速回顧。一般來說,遞歸可以解決的問題也一定可以用棧加循環的方式解決,畢竟遞歸實質上就是利用了棧嘛。

 

樹從根節點開始對每個結點進行遍歷,每個結點必定被訪問三次,如上圖。

第一次碰到就訪問就是先序,第二次碰到訪問就是中序,第三次碰到訪問就是后序。

public class TreeReader {

    public void preOrder(TreeNode t) {  //先序遍歷
        Stack<TreeNode> s = new Stack<>();
        while(t != null || !s.isEmpty()) {
            while(t != null) {
                System.out.println(t.val);  //第一次碰到就訪問結點
                s.push(t);                //壓棧保存,以便左邊訪問完了,之后可以彈出它得到它的右節點
                t = t.left;
            }
            if(!s.isEmpty()) {
                t = s.pop();
                t = t.right;      //當上述循環退出,說明左子樹全訪問完了,則把父結點彈出,准備訪問右子樹
            }
        }
    }

    public void inOrder(TreeNode t) {  //中序,與先序很像,只是在第二次碰到結點才訪問
        Stack<TreeNode> s = new Stack<>();
        while(t != null || !s.isEmpty()) {
            while(t != null) {
                s.push(t);      //第一次碰到不訪問,只保存
                t = t.left;
            }
            if(!s.isEmpty()) {
                t = s.pop();
                System.out.println(t.val);    //左子樹訪問完了,彈出父結點,第二次碰到則訪問
                t = t.right;            //准備訪問右子樹
            }
        }
    }

    public void lastOrder(TreeNode t) {  //后序與上述兩種方式有區別,只有左右結點都被訪問,當前結點才能被訪問
        TreeNode preNode = null;         //記錄上一次訪問結點
        Stack<TreeNode> s = new Stack<>();
        s.push(t);    //壓入根節點,這貨鐵定最后訪問,萬年棧底元素
        while(!s.isEmpty()) {
            TreeNode current = s.peek(); //獲取棧頂元素

            //當前結點左右結點均為空,或左右結點不都為空但已經都被訪問則當前結點可以訪問了(即上次訪問結點preNode == 左/右結點)
            //由於壓棧順序是先右后左,所以當前結點的孩子們訪問時必然是先左后右. preNode == left說明當前結點只有左孩子且已被訪問,==right說明有兩個孩子且左右均已訪問
            if((current.left==null&&current.right==null)||(preNode!=null&&(preNode==current.left||preNode==current.right))){
                System.out.println(current.val);
                s.pop();  
                preNode = current;  //每訪問一個結點都記錄一下,以便下次判斷結點的左右孩子是否已被訪問
            }
            else {
         if(current.right != null) //若不滿足上述條件,則把當前結點的孩子以先右后左的順序壓棧,這樣訪問時就是先左后右了   s.push(current.right);   if(current.left != null)   s.push(current.left);
       } } } }
class TreeNode { TreeNode left; TreeNode right; int val; public TreeNode(int val) { this.val = val; } }


免責聲明!

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



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