文章目录
- 1.二叉树相关基础知识
- 2.学习二叉树的作用
- 3.经典面试题-二叉树的基本操作
- 4.经典面试题-二叉树的进阶操作
-
- 4.1 [LC110 平衡二叉树](https://leetcode-cn.com/problems/balanced-binary-tree/)
- 4.2 [LC112 路径总和](https://leetcode-cn.com/problems/path-sum/)
- 4.3 [LC105 从前序和中序遍历序列构造二叉树](https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/)
- 4.4 [LC222 完全二叉树的节点个数](https://leetcode-cn.com/problems/count-complete-tree-nodes/)
- 4.5 [LC剑指Offer54 二叉搜索树的第k大节点](https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof/)
- 4.6 [LC剑指 Offer 26. 树的子结构](https://leetcode-cn.com/problems/shu-de-zi-jie-gou-lcof/)
1.二叉树相关基础知识
1.1 基础知识
基本概念:
-
0.二叉树的度是“出度“ 即箭头朝外的
-
1.树的每个节点都有唯一的父节点
-
2.每个节点的度最多为2
-
3.度为0的节点比度为2的节点多1个
-
4.n节点的树 有n-1条边
证明3——
n节点的树 有n-1条边
所以 节点数 = 边数+1
即为:
n0+n1+n2 (点数)= n1+2*n2+1(边数+1)
=====>n0 = n2 + 1 得证
1.2 二叉树的三种遍历方式
-
前序遍历
1 245 36 -
中序遍历
425 1 36
-
后序遍历
452 63 1
用这三个遍历能干啥?
拿着中序遍历的结果+前序/后序遍历的结果——
可以还原一棵二叉树
【1】先找到根节点
【2】拆出来 左子树中序遍历 左前 右中 右前
【3】再分别还原左子树 右子树
根据前序遍历和中序遍历结果 获得二叉树
前序 1 5 234
中序 5 1 324
前序 1 2 4 9 5 6 10 3 7 8
中序 4 9 2 10 6 5 1 3 8 7
【1】
【2】
【3】
1.3 特殊二叉树的性质
完全二叉树
可以使用连续的空间进行存储(数组)
没有度为1的节点,且所有度为0的叶子节点都在同一层
编号为i的子节点:
在完全二叉树中不需要记录子树的地址!
节省大量存储空间!!!!!
计算式和记录式
计算式节省空间——时间换空间
“需要节约空间一点的应用”——用计算式!
纪录是节省时间——空间换时间
数据结构改变的是我们思考问题的方法
培养数据结构思维!
满二叉树
没有(出)度为1 的 节点
1.4 关于树结构的深入理解
树的节点——代表集合
边——代表关系
2.学习二叉树的作用
2.1 学习二叉树的两大作用
作用一 理解 高级数据结构的基础
完全二叉树:
多叉树/森林:
二叉排序树:
- 红黑树 一类非常重要的二叉排序树
- B-(杠)树 B+树:文件系统 数据库 底层重要的数据结构
作用二 练习递归技巧的最佳选择
递归是空间换时间哦~
[重点思想]如何设计/理解递归程序?
-
数学归纳法——结构归纳法
-
赋予递归函数一个明确的意义
-
思考边界条件
-
实现递归过程
数学归纳法回顾
k0正确 假设ki是正确的 假设k(i+1)也是正确的
作用三 左孩子右兄弟表示法节省空间
把三叉树变为二叉树
为啥转化成二叉树要省空间啊?
举个栗子——
三叉树中每个节点存储3个指针域
有效指针域——指向明确节点的
例如这个三叉树 有63个指针域 其中有效指针域(具体的边)只有5个 浪费了13个指针域
而二叉树
6*2个指针域-5个有效指针域
浪费了7个
二叉树比三叉树香!
其中k元n叉树中的k和n越大 转换成二叉树越省空间!
n元k叉树浪费的空间——kn -(n-1)
n元2叉树浪费的空间——2n - (n-1)
**顺序存储结构和链式存储结构在二叉树上的优劣性
顺序:比较浪费空间(非完全二叉树时) 进行修改比较困难
读取某个结点的时候效率比较高
链式:空间占用小 容易修改
读取某个指定节点的时候效率偏低O(nlogn)
树形结构基本都是使用链式结构来进行存储
3.经典面试题-二叉树的基本操作
3.1 LC144 二叉树的前序遍历
class Solution {
public:
void preorder(TreeNode *root,vector<int> &res) {
if (root == NULL) return;
//如果碰到空节点 递归终止
//递归出口是必要的!
res.push_back(root->val); //将根节点值放到结果数组中
//res.push_back(xxx) 在数组res的末尾插入元素xxx
preorder(root->left,res); // 递归左子树
//递归的作用——把遍历到的那个root->val插入res的末尾
preorder(root->right,res); // 递归右子树
return;