算法讲解二叉树 Ⅰ
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.left
和root.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的左子树(左树)和右子树(右树)是否对称,那么左树和右树怎么判断对称呢,就左树的左子树和右树的右子树对称,且左树的右子树和右树的左子树对称,这样递归点不就出来了吗。
//题中给的函数签名
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为根的树展开成链表,那么就需要先将左右子树分别展开成链表,这样递归点不就出来了吗,最后进行拼接。
总结
对于二叉树的递归问题,一定要谨记以下两点~:
首先弄清楚函数的定义,并且相信这个函数可以完成任务,不要被递归绕进去。
写函数体的时候,只需关注当前节点root需要完成什么,往往你弄明白了root要完成什么,然后递归点就出来了。
当然,算法的理解需要一定的感觉,我相信当你见识过的题目多了,你就能理解我所强调的两个点,后续我还会出几篇关于二叉树的题解,敬请期待~
以上是关于算法讲解二叉树 Ⅰ的主要内容,如果未能解决你的问题,请参考以下文章