万字长文!二叉树入门和刷题看这篇就够了!

今天是小浩算法 “365刷题计划” 二叉树入门 - 整合篇。本篇作为入门整合篇,已经砍去难度较大的知识点,所有列出的内容,均为必须掌握。因为很长,写下目录:

  • 二叉树是啥

  • 二叉树的最大深度(DFS)

  • 二叉树的层次遍历(BFS)

  • 二叉搜索树验证

  • 二叉搜索树查找

  • 二叉搜索树删除

  • 平衡二叉树

  • 完全二叉树

  • 二叉树的剪枝

01

PART

二叉树是啥

二叉树有多重要?单就面试而言,在 leetcode 中二叉树相关的题目占据了300多道,近三分之一。同时,二叉树在整个算法板块中还起到承上启下的作用:不但是数组和链表的延伸,又可以作为图的基础。总之,非常重要!

什么是二叉树?官方是这样定义的:在计算机科学中,二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。

上面那是个玩笑,二叉树长这样:

二叉树常被用于实现二叉查找树二叉堆。树比链表稍微复杂,因为链表是线性数据结构,而树不是。树的问题很多都可以由广度优先搜索或深度优先搜索解决。

一般而言,我们会看到下面这些与树相关的术语:

小浩概念

与树相关的术语

树的结点(node):包含一个数据元素及若干指向子树的分支;

孩子结点(child node):结点的子树的根称为该结点的孩子;

双亲结点:B 结点是A 结点的孩子,则A结点是B 结点的双亲;

兄弟结点:同一双亲的孩子结点;堂兄结点:同一层上结点;

祖先结点: 从根到该结点的所经分支上的所有结点

子孙结点:以某结点为根的子树中任一结点都称为该结点的子孙

结点层:根结点的层定义为1;根的孩子为第二层结点,依此类推;

树的深度:树中最大的结点层

结点的度:结点子树的个数

树的度:树中最大的结点度。

叶子结点:也叫终端结点,是度为 0 的结点;

分枝结点:度不为0的结点;

有序树:子树有序的树,比如家族树;

无序树:不考虑子树的顺序;

了解了上面的基本概念之后。我们将通过几道例题,为大家引入树的经典操作。

02

PART

二叉树最大深度

复习上面的概念:树的深度指的是树中最大的结点层。

第104题:给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例:

给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

基本概念掌握:每个节点的深度与它左右子树的深度有关,且等于其左右子树最大深度值加上 1。即:

maxDepth(root) = max(maxDepth(root.left),maxDepth(root.right)) + 1

以 [3,9,20,null,null,15,7] 为例:

  • 我们要对根节点的最大深度求解,就要对其左右子树的深度进行求解

  • 我们看出。以4为根节点的子树没有左右节点,其深度为1。而以20为根节点的子树的深度,同样取决于它的左右子树深度。

  • 对于15和7的子树,我们可以一眼看出其深度为1。

  • 由此我们可以得到根节点的最大深度为

maxDepth(root-3)
=max(maxDepth(sub-4),maxDepth(sub-20))+1
=max(1,max(maxDepth(sub-15),maxDepth(sub-7))+1)+1
=max(1,max(1,1)+1)+1
=max(1,2)+1
=3

根据分析,我们通过递归进行求解:

 1//Go
 2func maxDepth(root *TreeNode) int {
 3    if root == nil {
 4        return 0
 5    }
 6    return max(maxDepth(root.Left), maxDepth(root.Right)) + 1
 7}
 8
 9func max(a int, b int) int {
10    if a > b {
11        return a
12    }
13    return b
14}

其实我们上面用的递归方式,本质上是使用了DFS的思想。所以这里就可以引出什么是DFS:深度优先搜索算法(Depth First Search),对于二叉树而言它沿着树的深度遍历树的节点,尽可能深的搜索树的分支这一过程一直进行到已发现从源节点可达的所有节点为止( 注意,这里的前提是对二叉树而言。DFS本身作为图算法的一种,在后续我会单独拉出来和回溯放一起讲。)

如上图二叉树,它的访问顺序为:

A-B-D-E-C-F-G

到这里,我们思考一个问题?虽然我们用递归的方式根据DFS的思想顺利完成了题目。但是这种方式的缺点却显而易见。因为在递归中,如果层级过深,我们很可能保存过多的临时变量,导致栈溢出。这也是为什么我们一般不在后台代码中使用递归的原因。如果不理解,下面我们详细说明:

事实上,函数调用的参数是通过栈空间来传递的,在调用过程中会占用线程的栈资源。而递归调用,只有走到最后的结束点后函数才能依次退出ÿ

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值