二叉樹遍歷基礎 -- 遞歸與非遞歸的實現方法

        以前也寫過很多關於二叉樹的東西了,可是整體來講,二叉樹仍是一個很繞的東西,因此單獨擇出來寫一篇筆記,以前也沒計劃什麼的,就想到什麼寫什麼吧。不過該篇文章的主要內容是關於二叉樹的三種遍歷(前序、中序、後序)不一樣的實現方式(遞歸與非遞歸)html

         首先,我以爲頗有必要去完全理解一下遞歸。node

         (1)遞歸的主體大概分兩部分:遞歸中止的條件、遞歸內容。spa

         (2)遞歸應用的實例:這個超級多,就好比最典型的斐波那契數列。我的認爲,能夠用循環實現的,遞歸基本上均可以實現,但有時遞歸的效率不如循環。code

         (3)遞歸又分爲單遞歸與多遞歸(二叉樹的三種遍歷遞歸方法均用到了雙遞歸!)htm

 

          根據上面的三點,舉個例子先。blog

          假設當x=0時,F(x)=1;不然F(x)=F(n-1)*n。這個時候就能夠用遞歸了,代碼實現以下。遞歸

class Solution{
     public int F(int n)
    {
        //遞歸中止條件
        if (n == 0)
        {
            return 1;
        }
        //遞歸內容
        else
       {
             return F(n - 1) * n;  
        } 
    }
}

        代碼分析一下以下:get

         二叉樹的三種遍歷:前序(根左右)、中序(左根右)、後序(左右根)it

        首先看三種遍歷的遞歸實現方法。(特色:代碼清晰,量少,但不易理解)io

        // (1)前序遍歷
        public TreeNode PreOrder(TreeNode pRoot)
        {
            //遞歸終止條件
            if (pRoot != null)
            {
                // 根->左->右
                Console.Write(pRoot.data + " ");
                PreOrder(pRoot.left);
                PreOrder(pRoot.right);
            }
        }

        // (2)中序遍歷
        public TreeNode MidOrder(TreeNode pRoot)
        {
            //遞歸終止條件
            if (node != null)
            {
                // 左->根->右
                MidOrder(pRoot.left);
                Console.Write(pRoot.data + " ");
                MidOrder(pRoot.right);
            }
        }

        // (3)後序遍歷
        public TreeNode PostOrder(TreeNode pRoot)
        {
            //遞歸終止條件
            if (pRoot != null)
            {
                // 左->右->根
                PostOrder(pRoot.left);
                PostOrder(pRoot.right);
                Console.Write(pRoot.data + " ");
            }
        } 

 分析:

        表達能力是多麼重要的事情啊,會是一回事,表述清楚又是一回事。難受啊,馬飛。。

        固然,我寫的東西可能有點亂,可是想表現幾個想法:

        遞歸和棧是密不可分的。

        上述三個方法均存在一個打印,兩個遞歸,可是惟一的區別就是順序的不一樣,因此,如何理解呢!!!關鍵點:若是打印在遞歸後面,則遞歸是不受打印影響的,也就是,我遞歸要先執行完,纔開始執行你打印,可是若是打印在遞歸前面,至關於打印已經屬於這個遞歸體了,沒次遞歸的時候都要執行一次打印!!!

        非遞歸下如何實現三種遍歷。

 

        // 前序遍歷
        public void PreOrderNoRecurise(Node<T> node)
        {
            if (node == null)
            {
                return;
            }
            // 定義一個棧存放數據
            Stack<Node<T>> stack = new Stack<Node<T>>();
            //把根節點放進去
            stack.Push(node);
            //定義一個空節點名稱
            Node<T> tempNode = null;

            while (stack.Count > 0)
            {
                // 根節點出棧打印
                tempNode = stack.Pop();
                Console.Write(tempNode.data);
                // 右子節點進棧
                if (tempNode.right != null)
                {
                    stack.Push(tempNode.right);
                }
                // 左子節點進棧
                if (tempNode.left != null)
                {
                    stack.Push(tempNode.left);
                }
            }
        }

        //中序遍歷
        public void MidOrderNoRecurise(Node<T> node)
        {
            if (node == null)
            {
                return;
            }
            // 定義一個棧存放數據
            Stack<Node<T>> stack = new Stack<Node<T>>();
            //定義一個空節點
            Node<T> tempNode = node;

            while (tempNode != null || stack.Count > 0)
            {
                // 左子節點進棧
                while(tempNode != null)
                {
                    stack.Push(tempNode);
                    tempNode = tempNode.left;
                }
                // 2.出棧遍歷節點並打印
                tempNode = stack.Pop();
                Console.Write(tempNode.data);
                // 3.左子節點遍歷結束則跳轉到右子節點
                tempNode = tempNode.right;
            }
        }

        //後序遍歷
        public void PostOrderNoRecurise(Node<T> node)
        {
            if (root == null)
            {
                return;
            }

            // 兩個棧:一個存儲,一個輸出
            Stack<Node<T>> stackIn = new Stack<Node<T>>();
            Stack<Node<T>> stackOut = new Stack<Node<T>>();
            Node<T> currentNode = null;
            // 根節點進棧
            stackIn.Push(node);
            // 左->右->根
            while (stackIn.Count > 0)
            {
                currentNode = stackIn.Pop();
                stackOut.Push(currentNode);
                // 左子節點進棧
                if (currentNode.left != null)
                {
                    stackIn.Push(currentNode.left);
                }
                // 右子節點進棧
                if (currentNode.right != null)
                {
                    stackIn.Push(currentNode.right);
                }
            }

            while (stackOut.Count > 0)
            {
                // 依次遍歷各節點
                Node<T> outNode = stackOut.Pop();
                Console.Write(outNode.data);
            }
        }

 

        非遞歸方法變化多樣,上述代碼借鑑Edison Zhou的文章,本身不想寫了,哈哈,可是道理並不難懂,不會向遞歸那樣難理解,有時間的時候再慢慢修改補充吧。。

相關文章
相關標籤/搜索