二叉树遍历基础 -- 递归与非递归的实现方法

        之前也写过不少关于二叉树的东西了,但是总体来说,二叉树还是一个很绕的东西,所以单独择出来写一篇笔记,之前也没计划什么的,就想到什么写什么吧。不过该篇文章的主要内容是关于二叉树的三种遍历(前序、中序、后序)不同的实现方式(递归与非递归)

         首先,我觉得很有必要去彻底理解一下递归。

         (1)递归的主体大概分两部分:递归停止的条件、递归内容。

         (2)递归应用的实例:这个超级多,就比如最典型的斐波那契数列。个人认为,可以用循环实现的,递归基本上都可以实现,但有时递归的效率不如循环。

         (3)递归又分为单递归与多递归(二叉树的三种遍历递归方法均用到了双递归!)

 

          根据上面的三点,举个例子先。

          假设当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;  
        } 
    }
}

        代码分析一下如下:

         二叉树的三种遍历:前序(根左右)、中序(左根右)、后序(左右根)

        首先看三种遍历的递归实现方法。(特点:代码清晰,量少,但不易理解)

        // (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的文章,自己不想写了,哈哈,但是道理并不难懂,不会向递归那样难理解,有时间的时候再慢慢修改补充吧。。

 

posted @ 2019-06-05 14:27  WeiMLing  阅读(511)  评论(0编辑  收藏  举报