二叉树的前序遍历与中序遍历,以及后序与中序遍历构建方法(迭代法)。

好久没有更新,。相信很多粉丝朋友们已经想我了。

今天用比较难的迭代法来讲讲二叉树的前序遍历与中序遍历,以及后序与中序遍历构建方法。

嘻嘻,不开玩笑了,最近由于找工作原因,在刷算法题,今天给大家带来两道硬核题,说是两道其实是一道,看到后面你就明白了。

相信很多代码人都有这样一个疑惑,那就是数据结构和算法是用来干嘛的,都说程序=数据结构+算法,我们程序员必须要掌握的基本功,树则作为一种基本类型,因为树能表示一对多的关系,比如上下级关系,更复杂一点的可能需要用到图,多对多,那么我们今天来学习一下树的构建方式。

相信很多同学都会遍历树,但是怎么构建树?我们这里讲的是利用迭代法,说白了也就是利用一个栈,什么是栈呢?就是利用一种先进,后出的数据结构,达到遍历一遍数组就构建二叉树的效果,具体怎么做?

这里我们看下面的图,这是力扣上的原图:

我们可以看到,图中的树三种遍历方式分别是:

先序遍历:3,9,20,15,7;(根左右)

中序遍历:9,3,15,20,7;(左根右)

后序遍历:9,15,7,20,3;(左右根)

三个特点都不用多说了,就是根的位置不同,而左每次都在右的左边,而右也在左的右边,哈哈,好有哲理的废话。总之,这个顺序是很简单的,不会写的同学可以跟作者一样,每次写之前将根左右,左根右写在上面在看着写,那么怎么去遍历构建树呢?

这里我们用到一个栈

这里作者比较懒,所以直接把结果和流程都画出来了,大家可以像我一样在纸上画一画,画明白了代码其实很好写,为什么呢?因为你画的时候有自己的一个理解和记忆,要不然是画不通的,我来讲一下我的思路,大家可以先不看代码,后面再去检验。

本着只讲干货的原则,我们开始正题:

首先,入栈先序数组第一个元素作为树节点,我们这里将栈里存放的是树节点,方便存取直接插入。

先序数组第一个入栈,中序数组指向第一个元素,进入循环,取先序数组第一个值作为插入节点的val,此时中序数组第一个跟栈顶元素比较,不相等则为左节点,左节点入栈,入栈的同时值为先序数组指针指向的值(这里确保我们每一次能检查上一次插入的节点,每次插入过后都需要入栈),之后略过else,

进入下一层循环,先序数组指针指向第第三个数,注意,这里比较的结果有所不同,由于我们比较的是中序指针指向的值和栈顶的值,此时if判断相等,进入else内容,里面有一个while循环,栈顶元素不为空而且与中序指针值相等,则指针自加指向下一个元素,同时pop出栈,这里有一个关键的点,就是需要用node去接住出栈的元素,这样跳出while循环时,我们才好插入,因为插入要在正确的位置。插入时,值还是为先序值,同时依旧的,插入指针入栈。

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode root = new TreeNode(preorder[0]);
        stack.push(root);
        int inorderindex = 0;
        for(int i = 1; i < preorder.length; i++){
            TreeNode node = stack.peek();
            int preorderval = preorder[i];
            if(node.val != inorder[inorderindex]){
                node.left = new TreeNode(preorderval);
                stack.push(node.left);
            } else {
                while(!stack.isEmpty() && inorder[inorderindex] == stack.peek().val) {
                    inorderindex++;
                    node = stack.pop();
                }
                node.right = new TreeNode(preorderval);
                stack.push(node.right);
            }
        }
        return root;
    }
}

这样简单的一个先序和中序遍历就写好了,我们可以看一下效果。pass!

那么我们回想一下为什么,首先栈顶元素必定是刚插入过的元素,我们判断中序和栈顶无非是想确认左右节点,那么只要中序指针和栈顶不相等,就为左节点,这是为什么呢,因为先序是中左右,而中序是左中右,我们一旦选择了中,那么剩下的肯定是先会遇见左,此时只要不为中,就是左,遇到中以后说明左以及完了,此时的节点是右也是中,他是下一轮循环的始,也就是中,也是上一轮循环的末,也就是右。此时,我们就知道了,利用栈来写的好处。

那么懂了先序遍历和中序遍历,我们来看看后序遍历有什么不一样。

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode root = new TreeNode(postorder[postorder.length-1]);
        stack.push(root);
        int inorderindex = postorder.length - 1;
        for(int i = postorder.length - 2; i >= 0; i--){
            int postorderval = postorder[i];
            TreeNode node = stack.peek();
            if(node.val != inorder[inorderindex]){
                node.right = new TreeNode(postorderval);
                stack.push(node.right);
            } else {
                while(!stack.isEmpty() && stack.peek().val == inorder[inorderindex]){
                    inorderindex--;
                    node = stack.pop();
                }
                node.left = new TreeNode(postorderval);
                stack.push(node.left);
            }
        }
        return root;
    }
}

可以清晰的看出来,后序的结构和原理和前序是一模一样的,我们只要将阴阳反转,就能写出效果一模一样的代码。作者当时写的时候看了一眼就开始自己改了,写完发现竟然思路完全正确,可见这个写法的巧妙之处。

浅说一下,首先,从后往前,什么叫从后往前,原来从前往后是指for循环,从前序的前面和中序的前面第一个开始遍历,同时先插入左节点,此刻我们相反,从后序的后面和中序的后面最后一个开始遍历,同时先插入右节点,带着这样的思路,就可以写出后序的代码。

可以看到,前序和后序除了循环方向和插入顺序不一样,其他地方几乎是一样的,只要弄清楚了原理,就可以同时掌握这两种方式。

怎么样,是不是很简单,快动手自己画画吧,很快就能掌握清楚了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值