第六部分 二叉树
1. 理论基础
- 二叉树种类:
- 满二叉树:只有度为0的结点和度为2的结点,并且度为0的结点在同一层上
- 完全二叉树:除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。优先级队列其实是一个堆,堆就是一棵完全二叉树,同时保证父子节点的顺序关系。
- 二叉搜索树:二叉搜索树是有数值的有序树。若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉排序树
- 平衡二叉搜索树:又被称为AVL(Adelson-Velsky and Landis)树,它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
- 二叉树的存储方式
- 顺序存储:如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2
- 链式存储:通过指针连接
- 二叉树的遍历方式
- 深度优先遍历
- 前序遍历:中左右(递归法,迭代法)
- 中序遍历:左中右(递归法,迭代法)
- 后序遍历:左右中(递归法,迭代法)
栈其实就是递归的一种实现结构,前中后序遍历借助栈使用递归方式实现。
- 广度优先遍历(迭代法)
借助队列实现。
- 二叉树的定义
class TreeNode {
constructor()
};
function TreeNode2(val, left, right) {
this.val = (val===undefined ? 0 : val);
this.left = (left===undefined ? null : left)
this.right = (right===undefined ? null : right)
}
2. 递归遍历
- 递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
- 编写递归算法的三要素:
- 递归函数的参数和返回值
- 递归的终止条件
- 单层递归的逻辑
- 实现
var preorderTraversal = function(root) {
let res = [];
const dfs = function(root) {
if(root === null) {
return
}
res.push(root.val);
dfs(root.left);
dfs(root.right);
dfs(root.left);
res.push(root.val);
dfs(root.right);
dfs(root.left);
dfs(root.right);
res.push(root.val);
}
dfs(root);
return res;
};
3. 迭代遍历
- 前序遍历迭代
- 中序遍历迭代
- 后序遍历迭代
4. 统一迭代
5. 层序遍历
var levelOrder = function(root) {
const ret = [];
if (!root) {
return ret;
}
const q = [];
q.push(root);
while (q.length !== 0) {
const currentLevelSize = q.length;
ret.push([]);
for (let i = 1; i <= currentLevelSize; ++i) {
const node = q.shift();
ret[ret.length - 1].push(node.val);
if (node.left) q.push(node.left);
if (node.right) q.push(node.right);
}
}
return ret;
};