算法讲解二叉树 Ⅰ

Posted 离小猿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法讲解二叉树 Ⅰ相关的知识,希望对你有一定的参考价值。

二叉树模板介绍


话不多说,先放出二叉树遍历的框架吧~

 //二叉树遍历框架 void traverse(TreeNode root) { //base case if(root == null)            return;        //前序遍历,如果是前序遍历在这里写逻辑 traverse(root.left);        //中序遍历,如果是中序遍历在这里写逻辑 traverse(root.right);        //后序遍历,如果是后序遍历在这里写逻辑 }

同学们都知道二叉树遍历其实就是一个递归算法,所以在遇到二叉树问题的时候我们都可以在此模板上进行拓展。


下面这段引用自labuladong大佬,把二叉树中的递归讲的非常好

labuladong,公众号:labuladong

写递归算法的关键是要明确函数的「定义」是什么,然后相信这个定义,利用这个定义推导最终结果,绝不要试图跳入递归。---讲的真的太好了。

怎么理解呢,我们用一个具体的例子来说,比如说让你计算一棵二叉树共有几个节点:

// 定义:count(root) 返回以 root 为根的树有多少节点int count(TreeNode root) { // base case if (root == null) return 0; // 自己加上子树的节点数就是整棵树的节点数 return 1 + count(root.left) + count(root.right);}

这个问题非常简单,大家应该都会写这段代码,root本身就是一个节点,加上左右子树的节点数就是以root为根的树的节点总数。

左右子树的节点数怎么算?其实就是计算根为root.leftroot.right两棵树的节点数呗,按照定义,递归调用count函数即可算出来。

写树相关的算法,简单说就是,先搞清楚当前root节点该做什么,然后根据函数定义递归调用子节点,递归调用会让孩子节点做相同的事情。



实践一下


1、对称二叉树【leetcode 101题 easy】

//题中给的函数签名public boolean isSymmetric(TreeNode root);


首先你看题目的定义:检查二叉树是否镜像对称,这时候你直接分析root节点要干什么:

    如果二叉树对称,就要求root的左子树和右子树对称【这是切入点】,为了方便我就直接称左子树和右子树分别为左树(left)和右树(right)吧。


我们定义下面是是判断左树和右树是否对称的方法;


boolean symmetic(TreeNode left, TreeNode right);


那么如何证明左树和右树对称呢,我们来看下面这一张图,左树和右树对称需满足以下条件:

算法讲解【二叉树 Ⅰ】


    1.首先left和right的值要相等,如果left和right的val值都不想等,那肯定不对称吧,所以这一条件符合:

           left.val == right.val

    2.然后比较left和right的子树,从图中可以看出如果left和fight对称,那么left的左子树要跟right的右子树对称,并且left的右子树和right的左子树要对称:

symmetric(left.left, right.right) && symmetric(left.rught, right.left) 

         你看这不就找到递归点了吗?                        


好了,思路这不就来了嘛,直接上代码吧~

 public boolean isSymmetric(TreeNode root) { // 如果根节点为空,则对称 if (root == null) return true; return symmetric(root.left, root.right); }
public boolean symmetric(TreeNode left, TreeNode right) { // 1.如果left和right都是null,那么这两个就是对称的 if (left == null && right == null) { return true; } // 2.如果left和right中只有一个为null,另一个不是null,则不对称 if (left == null || right == null) { return false; } // 3.如果left和right都不为null // 3.1如果left的val值和right的val值相等 if (left.val == right.val) { // 则需要看看,left的左子树是否跟right的右子树对称。且left的右子树是否和right的左子树对称 // 这里是递归点 return symmetric(left.left, right.right) && symmetric(left.right, right.left); } return false; }

我们只需要专注于当前root节点要做什么,在本题中如果要判断以root为根的树是否对称,那么就需要判断root的左子树(左树)和右子树(右树)是否对称,那么左树和右树怎么判断对称呢,就左树的左子树和右树的右子树对称,且左树的右子树和右树的左子树对称,这样递归点不就出来了吗。


2、二叉树展开为链表 【leetcode 114题 medium】

//题中给的函数签名void flatten(TreeNode root)

首先看到这个题目,你会发现flatten(TreeNode root)是将以root为根节点的二叉树,拉成一个向右下方链表,首先你要相信这个函数真的可以将二叉树拉成这个链表,那么将一个以root为根的二叉树展开为链表,步骤如下:


    1.先将root的左子树和右子树分别展开为链表。


    2.将左子树变成root的右子树,并将原来的右子树插到末端。



谨记:你要相信void flatten(TreeNode root)这个函数真的可以将二叉树拉成这个链表,所以第一步直接调用对左子树和右子树调用这个函数。

 public void flatten(TreeNode root) {        //base case if (root == null) return;        //1、分别将左子树和右子树展开成链表 flatten(root.left); flatten(root.right);
        //2、将左子树变成root的右子树,并将原来的右子树插到末端        TreeNode tmp = root.right; root.right = root.left; root.left = null;
        //将原先右子树插到当前右子树的末端 while(root.right != null) root = root.right; root.right = tmp;
    }

我们只需要专注于当前root节点要做什么,在本题中如果要将以root为根的树展开成链表,那么就需要先将左右子树分别展开成链表,这样递归点不就出来了吗,最后进行拼接。


总结


对于二叉树的递归问题,一定要谨记以下两点~:

  1. 首先弄清楚函数的定义,并且相信这个函数可以完成任务,不要被递归绕进去。

  2. 写函数体的时候,只需关注当前节点root需要完成什么,往往你弄明白了root要完成什么,然后递归点就出来了。


当然,算法的理解需要一定的感觉,我相信当你见识过的题目多了,你就能理解我所强调的两个点,后续我还会出几篇关于二叉树的题解,敬请期待~


以上是关于算法讲解二叉树 Ⅰ的主要内容,如果未能解决你的问题,请参考以下文章

java数据结构二叉树查找结点操作,递归调用求详细讲解

算法大根堆

不再惧怕!二叉树结构相关算法总结 | 原力计划

树の讲解-----二叉树入门(遍历)

左老师休整一周后又回来啦,今晚八点为你讲解二叉树布隆过滤器哈希算法

平衡二叉树讲解