二叉树的种类
满二叉树和完全二叉树
满二叉树
如果一棵二叉树只有度为0的节点和度为2的节点,并且度为0的节点在同一层上,
深度为k 有2的k次方-1个节点
完全二叉树
除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面的节点都集中在该层最左边的若干位置。
二叉搜索树
二叉搜索树有数值,是一个有序树。
若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值
若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值
它的左右子树也分别为二叉排序树
平衡二叉搜索树
平衡二叉搜索树:又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:,
它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树,并且左右两个子树都是一棵平衡二叉树。
存储方式
二叉树可以链式存储,也可以顺序存储。
链式存储用指针,顺序存储用数组
顺序存储的元素在内存中是连续分布的,链式存储则是把分布在各个地址的节点串联一起
用数组来存储二叉树如何遍历的呢?
如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。
遍历方式
主要有两种遍历方式:
1 深度优先遍历:先往深走,遇到叶子节点再往回走
2 广度优先遍历:一层一层的去遍历
那么从深度优先遍历和广度优先遍历进一步拓展,才有如下遍历方式:
- 深度优先遍历
- 前序遍历(递归法,迭代法)
- 中序遍历(递归法,迭代法)
- 后序遍历(递归法,迭代法)
- 广度优先遍历
- 层次遍历(迭代法)
在深度优先遍历中:有三个顺序,前中后序遍历,指的是中间节点的遍历顺序
经常会使用递归的方式来实现深度优先遍历,也就是实现前中后序遍历,使用递归是比较方便的。
广度优先遍历的实现一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。
说到二叉树,就不得不说递归
function TreeNode(val, left, right) {
this.val = (val===undefined ? 0 : val)
this.left = (left===undefined ? null : left)
this.right = (right===undefined ? null : right)
}
二叉树的定义
链式存储的二叉树节点的定义方式
二叉树的定义 和链表是差不多的,相对于链表 ,二叉树的节点里多了一个指针, 有两个指针,指向左右孩子。
二叉树的递归遍历
前后中序的递归写法
通过简单题目把方法论确定下来,有了方法论,后面才能应付复杂的递归。
递归算法的三个要素。每次写递归,都按照这三要素来写,可以保证大家写出正确的递归算法!
确定递归函数的参数和返回值:
确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型
确定终止条件:写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
确定单层递归的逻辑
确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
前中后序遍历
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
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)
return res
};