一、树的相关概念
- 高度与深度
- 节点的深度是指从根节点(深度为1)开始自顶向下逐层累加至该节点时的深度值
- 节点的高度是指从最底层叶子节点(高度为1)开始自底向上逐层累加至该节点时的高度值
- 树的深度是指树中节点的最大深度,树的高度是指树中节点的最大高度。对树而言,深度和高度是相等的。
- 完全二叉树
- 完全二叉树可以通过建立一个大小为2的k次方的数组来存放所有节点的信息,其中k为完全二叉树的最大高度
- 且1号位存放的必须是根节点,这样就可以用数组下标来表示节点编号
- 该数组中元素存放的顺序恰好为该完全二叉树的层序遍历序列。对其中的任何一个节点(其编号为x),其左孩子编号一定为2x,右孩子编号一定为2x+1
- 判断某个节点是否为叶节点的标志为:该节点(记下标为root)的左子节点的编号root*2大于节点总个数n
- 判断某个节点是否为空节点的标志为:该节点下标root大于节点总个数n
- 二叉搜索树
- 左子树上所有节点的数据域均小于或等于根节点的数据域,右子树上所有节点的数据域均大于根节点的数据域
- 二叉搜索树的删除操作
- 为了保证删除后的树依然是一棵二叉搜索树,有两种方法。一是找到它中序遍历的前驱节点,将它的值赋值给待删除节点,再删除前驱节点;另一种是找到中序遍历它的后继节点,将它的值赋给待删除节点,再删除后继节点
- 较简单的实现方法中,删除节点的方式是递归调用删除函数,但是该方法花销大,优化后不用递归调用删除函数,可以直接删除。
- 要注意,总是优先删除前驱或后继,容易导致树的左右子树高度极度不平衡,使得二叉搜索树退化成一条链。解决方法有两种,一种是每次交替删除前驱或后继;另一种是记录子树高度,总是优先在高度较高的一棵子树里删除节点。
- AVL树
- 并查集
- 堆
- 哈夫曼树
二、树的遍历
- 题目:二叉树的前序/中序/后序遍历
- 基础版:递归版
- 常规版:迭代版,栈辅助实现(三种统一版,标记法)
- 进阶版:morris法,O(1)的空间复杂度
- 题目:二叉树的层序遍历
- 迭代版,BFS队列辅助实现
- 递归版
- 题目:二叉树的层序遍历,自下而上的层序遍历
- 迭代版,BFS辅助队列实现,在I的基础上返回前加一行reverse
- 递归版,同迭代版
- 题目:二叉树的锯齿形层序遍历,即先从左往右,再从右往左,以此类推
- 迭代版:队列实现,添加一个bool变量判断该层遍历是否要reverse
- 递归版,在层序遍历的基础设添加一个bool参数,根据它判断是否要reverse
- 题目:二叉搜索树中有两个节点被交换,导致有序性被破坏,恢复原来的二叉搜索树
- O(n)版:采用栈+双指针,或数组+双指针
- O(1)版:莫里斯算法遍历树+双指针
- 该题中可能有两组逆序对或一组逆序对,注意双指针的赋值方式
六、100相同的树
- 题目,给两棵树的根节点,判断这两棵树是否相同
- 迭代版:栈,每次将两棵树的对应位置节点入栈,栈非空则每次连续弹栈两次,判断是否一致
- 递归版:如果二者均为空,则返回true,否则如果一方为空,返回false,否则如果值不相等,返回false,否则递归调用判断该节点的孩子是否相同
- 题目:给一棵树,判断该树是否为对称二叉树
- 迭代版:栈,每次判断栈顶的两个元素,即对称位置的两个元素。如果相同就将它们对应的孩子先后入栈
- 递归版:传入的参数为对应的两个位置的指针
- 判断一棵树是否为平衡二叉树
- 思路:如果它的左子树是平衡二叉树且右子树是平衡二叉树,且左右子树的高度差小于等于1,就是平衡二叉树
- 递归法:自顶向下和自底向上两组判断方法
九、114 二叉树展开为链表 - 题目,将一棵二叉树按展开为链表,链表中节点的顺序对应二叉树的前序遍历
- 思路:对每个节点,将它的右子树接到它的左子树的最右节点后,再修改当前节点的右孩子为左孩子,左孩子为空,处理链表的下一个节点
- O(1) 迭代法:类似莫里斯算法
- O(logn)递归法:将迭代转化为递归
- O(n)解法:栈辅助前序遍历
- 题目:一棵完美二叉树,每个节点都有next指针,填它的next指针
- 递归版1:利用上一层的next指针填这一层的next指针
- 递归版2:对一个节点,它的左孩子不断往右,右孩子不断往左,它们在同一层中始终是相邻的
- O(1)迭代版:借助最左链遍历每一层
- 题目:一棵树,每个节点都有next指针,填它的next指针
- 思路:将每一层看作一个next指针连接起来的链表,利用该链表,填下一层的next指针
- 递归版和迭代版,pre指向root所在层,cur为下一层,也是当前在填next指针的层
三、二叉树的构建
- 题目:从前序遍历与中序遍历序列构造二叉树
- 递归实现,注意传入的参数设置,下标还是迭代器,是否要传数组
- 优化:空间换时间,降低每次find的开销,采用unordered_map存储中序遍历序列中元素与其对应下标
- 可写成模板函数
- 题目:从后序遍历与中序遍历序列构建二叉树
- 递归实现,同上
十四、96 不同的二叉搜索树
- 题目:给数字n,求n个节点组成的二叉搜索树有多少种
- 求递推公式,动态规划
- 题目:给数字n,求n个记得组成的二叉搜索树哪些,返回储存根节点的链表
- 递归实现,for循环中计算以k为根的子树,再递归计算它的左右子树,最后组合不同的子树
- 优化:记忆化
十六、98 验证二叉搜索树
- 题目:给二叉树的根节点,判断它是否为二叉搜索树
- 递归1:二叉树的中序遍历有序,存储遍历中的pre,遍历时判断是否递增,否则返回false
- 二叉搜索树的判断条件是左子树是二叉搜索树、右子树是二叉搜索树以及左子树都小于当前节点值右子树都大于当前节点值。
- 递归2:传入参数为根节点以及数据范围,递归判断是否为二叉搜索树
- 题目:给有序数组,将它转化为一棵高度平衡的二叉搜索树
- 每次选区间的中间节点,建立的二叉搜索树就是高度平衡的二叉搜索树
- 递归建树
十八、109 有序链表转二叉树
- 题目:给有序链表,将它转化为一棵高度平衡的二叉树
- 递归1:同有序数组,自顶向下建树,区别是将取中间节点单独写成一个函数,采用双指针法实现
- 递归2:自底向上建树,利用二叉树的中序遍历结果为链表,采用引用修改链表指针位置
四、二叉树的递归
十九、111 二叉树的最小深度
- 题目:给一棵二叉树,返回它的最小深度
- 二叉树的最小深度与最大深度不同,如果它没有左子树,最大深度一定在右子树上,最小深度也一定在右子树上
- 递归或迭代辅助栈实现
二十、104 二叉树的最大深度
- 题目:给一棵二叉树,返回它的最大深度
- 递归实现
- 迭代实现二叉树的层序遍历
二十一、112 路径总和
- 题目:给一棵二叉树和一个目标值,判断有没有从根节点到叶子节点的路径和为给定值
- 由于给定值可以为负,不能剪枝
- 根节点为空时返回false,故要在叶子节点处判断
二十二、113 路径总和II
- 题目:给一棵二叉树和一个目标值,返回符合要求的所有从根节点到叶子节点的路径
- 递归实现DFS,要在叶子节点处判断是否符合条件
二十四、124 二叉树中的最大路径和
- 题目:给一棵二叉树,求从任意节点出发到达任意节点的最大路径和是多少
- 类比最大连续子序列和,比较以每一个节点为根,它的左子树中的最大路径和与右子树中的最大路径和是否比当前的最大路径和大,是则更新当前的最大值
- 题目:给一棵二叉树,每一个从根节点到叶子节点的路径可以组成一个整数,求所有组成的整数的和
- DFS实现,先思考递归边界,再处理一般节点