二叉树-百度百科:
二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个结点最多只能有两棵子树,且有左右之分 。
二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个结点 。
二叉树的性质:
- 第(i)层最多有(2^{i - 1})个节点
- 二叉树深度为(k),最多有(2^{k - 1})个节点
- 满二叉树:上图中即为一个满二叉树
- 完全二叉树:出最后一层外为满二叉树,最后一层叶子节点自左向右依次排列
二叉树的遍历
//ES6
class Node {
constructor(val){
this.val = val;
this.left = null;
this.right = null;
}
}
class BinaryTree {
constructor() {
this.root = null;
}
addNode(val) {
let curNode = new Node(val);
if (!this.root) this.root = curNode;
else {//层序遍历,找到空节点加入
let q = [this.root];
while (q.length) {
let cur = q.shift();
if(cur.left) {
q.push(cur.left);
} else {
cur.left = curNode;
break;
}
if (cur_right) {
q.push(cur_right);
} else {
cur_right = curNode;
break;
}
}
}
}
}
-
前序遍历
- 节点的访问顺序为: 根节点 --> 左子节点 --> 右子节点
- 先访问当前节点
- 访问当前节点的左子节点
- 重复2,直到当前节点为空,返回上一层状态,访问其右子节点,重复2
-
- 递归实现
preOrder(node) {
if (node) {
console.log(node.val);
this.preOrder(node.left);
this.preOrder(node.right);
}
}
-
-
非递归实现
+ 利用栈的性质,先进后出,根据前序遍历的顺序 根 -> 左 -> 右 1. 初始化栈,root入栈 2. 栈顶元素出栈,记录其value 3. 右子节点存在则入栈 4. 左子节点存在则入栈 5. 重复2,3,4直到栈空
-
preOrder() {
let goal = [],
idx = 0,
sta = [this.root];//stack
while (sta.length) {
let cur = sta.pop();
goal[idx++] = cur.val;
if (cur.right) sta.push(cur.right);
if (cur.left) sta.push(cur.left);
}
console.log(goal);
}
-
中序遍历:左子节点 -> 根节点 -> 右子节点
- 递归写法同上,修改顺序即可
- 非递归:
- 根节点入栈
- 判断栈顶元素左子节点是否存在
- 存在,继续入栈左子节点
- 不存在,弹栈,记录value,若有右子节点存在则入栈
- 重复2,直到栈空
inOrder() {
let goal = [],
idx = 0,
cur = this.root,
sta = [];//stack
if (!this.root) console.log("Empty!");
while (sta.length || cur) {
while (cur) {//存在则入栈
sta.push(cur);
cur = cur.left;
}
cur = sta.pop();//top
goal[idx++] = cur.val;
cur = cur.right;//修改cur为当前节点的右子节点,之后若存在则入栈
}
console.log(goal);
}
-
后序遍历: 左子节点 -> 右子节点 -> 根节点
- 递归写法同上,修改顺序即可
- 需要保证在左子树、右子树访问结束后在访问根节点 -- 加标记,记录根节点访问次数, == 2即弹出
- 根节点入栈
- 左子节点入栈 ...-> 叶子结点
- 弹栈,记录叶子结点,叶子结点父节点次数 + 1,右子节点入栈
- 栈顶继续2,3
//待填坑。。。
-
- 法2:使用双栈,前序遍历压栈顺序为先右后左,其遍历结果为:根 -> 左 -> 右,若压栈顺序为先左后右则遍历结果为:根 -> 右 -> 左,恰好为后序遍历的倒叙,第一次遍历结果使用另一个栈存储,最后弹栈输出即可
lastOrder() {
let goal = [],
sta1 = [this.root],
sta2 = [];
console.log("LastOrder:");
while (sta1.length) {
let cur = sta1.pop();
sta2.push(cur.val);
if (cur.left) sta1.push(cur.left);
if (cur.right) sta1.push(cur.right);
}
while (sta2.length) {
goal.push(sta2.pop());
}
console.log(goal);
}
- 层序遍历:使用队列即可,先进先出。初始root入队,每次记录队首信息,存储队首的左右子节点即可。
leverOrder() {
let goal = [],
idx = 0,
q = [this.root];
console.log("LeverOrder:");
while (q.length) {
let cur = q.shift();//取出队首
//按层遍历记录每次的q.length即为当前层的节点数
// let len = q.length;
// for (let i = 0; i < len; i++){
// let cur = q.shift();
// if(...) ...
// }
goal[idx++] = cur.val;
if (cur.left) q.push(cur.left);
if (cur.right) q.push(cur.right);
}
console.log(goal);
}
- 二叉搜索树:左子节点的值小于根节点的值,右子节点的值大于根节点的值
- JavaScript实现
class BinarySearchTree {
constructor () {
this.root = null;
}
addNode(data) {//相对于父节点的值,左子节点小,右子节点大
if (this.root === null) this.root = new Node(data);
else {
let curNode = new Node(data);
let cur = this.root;
while (true) {
if (data < cur.val) {
if (!cur.left) {
cur.left = curNode;
break;
} else {
cur = cur.left;
}
} else {
if (!cur.right) {
cur.right = curNode;
break;
} else {
cur = cur.right;
}
}
}
}
}
inOrder() {//二叉搜索树的中序遍历结果为升序
let cur = this.root,
st = [],
book = [];
while (cur || st.length) {
while (cur) {
st.push(cur);
cur = cur.left;
}
cur = st.pop();
book.push(cur.val);
cur = cur.right;
}
console.log(`BST: ${book}`);
}
}