前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >二叉树遍历基础 -- 递归与非递归的实现方法

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

作者头像
WeiMLing
发布2019-08-23 19:41:03
8620
发布2019-08-23 19:41:03
举报
文章被收录于专栏:WeiMLingWeiMLing

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

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

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

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

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

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

假设当x=0时,F(x)=1;否则F(x)=F(n-1)*n。这个时候就可以用递归了,代码实现如下。

代码语言:javascript
复制
class Solution{
     public int F(int n)
    {
        //递归停止条件
        if (n == 0)
        {
            return 1;
        }
        //递归内容
        else
       {
             return F(n - 1) * n;  
        } 
    }
}

代码分析一下如下:

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

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

代码语言:javascript
复制
        // (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 + " ");
            }
        } 

分析:

表达能力是多么重要的事情啊,会是一回事,表述清楚又是一回事。难受啊,马飞。。

当然,我写的东西可能有点乱,但是想表现几个想法:

递归和栈是密不可分的。

上述三个方法均存在一个打印,两个递归,但是唯一的区别就是顺序的不同,所以,如何理解呢!!!关键点:如果打印在递归后面,则递归是不受打印影响的,也就是,我递归要先执行完,才开始执行你打印,但是如果打印在递归前面,相当于打印已经属于这个递归体了,没次递归的时候都要执行一次打印!!!

非递归下如何实现三种遍历。

代码语言:javascript
复制
        // 前序遍历
        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的文章,自己不想写了,哈哈,但是道理并不难懂,不会向递归那样难理解,有时间的时候再慢慢修改补充吧。。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-06-05 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档