1.满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。
如图所示,这棵二叉树深度为k,有2^k-1个节点。
2.完全二叉树:除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。
举例说明:
优先级队列其实是一个堆,堆就是一棵完全二叉树,同时保证父子节点的顺序关系。
3.二叉搜索树:有数值的,并且是一个有序树,定义为
举例两棵搜索树:
4.平衡二叉搜索树:又被称为AVL(Adelson-Velsky and Landis)树,它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
举例说明:
补充知识点:
C++中map、set、multimap,multiset的底层实现都是平衡二叉搜索树,所以map、set的增删操作时间时间复杂度是logn,而unordered_map、unordered_set,unordered_map、unordered_set底层实现是哈希表。
链式存储:使用指针。
通过指针把分布在各个地址的节点串联一起,如图所示。
链式存储更有利于我们理解,因此二叉树一般都是用链式存储的。
顺序存储:使用数组。
用数组来存储二叉树,如图所示。
数组存储的二叉树的遍历方式:
如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。
二叉树主要有两种遍历方式:
这两种遍历是图论中最基本的两种遍历方式。
从深度优先遍历和广度优先遍历进一步拓展,有如下遍历方式:
这里前中后,其实指的就是中间节点的遍历顺序。
举例说明:
补充知识:
实现前中后序遍历,使用递归是比较方便的。
之前我们讲栈与队列的时候,就说过栈其实就是递归的一种实现结构,也就说前中后序遍历的逻辑其实都是可以借助栈使用递归的方式来实现的。
而广度优先遍历的实现一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。
C++中,链式存储的二叉树节点的定义方式:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
相对于链表 ,二叉树的节点里多了一个指针, 有两个指针,指向左右孩子。
递归算法的三要素:
1.确定递归函数的参数和返回值:确定哪些参数在递归的过程中需要被处理并加入到递归函数里,明确递归的返回值和递归函数的返回类型。
2.确定终止条件:没写对终止条件会导致内存栈溢出(操作系统用一个栈的结构来保存每一层递归的信息)。
3.确定单层递归的逻辑:确定每一层递归需要处理的信息,这里会重复调用自己来实现递归的过程。
以前序遍历为例:
1.确定递归的参数和返回值:参数里需要传入vector来存放节点数值,除此之外不需要在处理数据也不需要返回值,因此函数返回类型为void,代码如下
void traversal(TreeNode* cur, vector& vec)
2.确定终止条件:当遍历的节点为空,那么本层递归结束,直接return,代码如下
if (cur == NULL) return;
3.确定单层递归的逻辑:前序遍历是中左右的顺序,代码如下
vec.push_back(cur->val); //中
traversal(cur->left, vec); //左
tarversal(cur->right, vec); //右
前序遍历的完整代码:
class Solution {
public:
void traversal(TreeNode* cur, vector% vec){
if (cur == NULL) return;
vec.push_back(cur->val);
traversal(cur->left, vec);
traversal(cur->right, vec);
}
vector preoderTraversal(TreeNode* root){
vector result;
traversal(root, result);
return result;
}
};
中序遍历:
void traversal(TreeNode* cur, vector& vec){
if (cur == NULL) return;
traversal(cur->left, vec); //左
vec.push_back(cur->val); //中
traversal(cur->right, vec); //右
}
后序遍历:
void traversal(TreeNode* cur, vector& vec){
if (cur == NULL) return;
traversal(cur->left, vec); //左
traversal(cur->right, vec); //右
vec.push_back(cur->val); //中
}