二叉树
- 网站 esprima 返回语法树
空二叉树 没有节点 null 有一个根节点,左子树,右子树 二叉树结点左右子树为空 称为叶子结点
除最后两排 都有左右子树 称为完全二叉树
除最后一排 都有左右子树 称为满二叉树 每一层结点数量呈等比数列
计算二叉树结构 log2()
完全二叉树 叶子结点和总结点数量关系
二叉树指向null指针总比节点数量多一
n个结点 指针数量2n
二叉树的存储方式
对象
tnode = { val: 3, lfet: null, right: null }
tnode.lfet = { val: 5, lfet: null, right: null }
tnode.right = { val: 2, lfet: null, right: null }
数组
- 用null补成完全二叉树 按序存储数组
- 下标 n left 2n + 1 right 2n + 2 反推[2n - 1}/2 下取整
- 缺点:非完全二叉树 null更多
将数组存储二叉树转换为链式表达
function createTreeNode(val) {
return {
val: val,
left: null,
right: null,
}
}
//将用数组中根节点在i位置的树表达的二叉树转换为用链式表达的二叉树
function ary2tree(ary, i = 0) {
if (ary[i] == null) {
return null
}//超出数组范围undefined == null 如果为null 他的子节点不会再展开
var root = createTreeNode(ary[i])
root.left = ary2tree(ary, 2 * i + 1)
root.right = ary2tree(ary, 2 * i + 2)
return root
}
####将使用链式表达法表达的二叉树转换为数组表达方式
function tree2ary(root, ary = [], idx = 0) {
if (root == null) {
//ary[idx] = null 可以填null 没有empty 但是空数组会返回[null]
return []
}
ary[idx] = root.val
tree2ary(root.left, ary, idx * 2 + 1)
tree2ary(root.right, ary, idx * 2 + 2)
return ary
}
tree =ary2tree([1,2,3,null,4,7,null,null,5,null,6,null,null])
tree2ary(tree)
//[1, 2, 3, empty, 4, 7, empty × 4, 6]
tree2ary(ary2tree([1,2,3,4,undefined,4,7,,,,,6]))
//[1, 2, 3, 4, empty, 4, 7, empty × 4, 6]
//ary = [1, , null, undefined]
//浏览器里 没有1下标 empty
var b = []
b[1] = 0
// b [empty,1]
将二叉树转换为紧凑型表示的数组
- 只补必要的Null null延伸的null不补了
function treeToAry(root) {
if (!root) {
return []
}
var result = []
var nextRow = [] //按顺序存储下一行结点
var nodes = [root]
while (nodes.length) {
for (var i = 0; i < nodes.length; ++i) {
if (nodes[i] == null) {
result.push(null)
} else {
result.push(nodes[i].val) //nodes空 null.val 不行
nextRow.push(nodes[i].left)
nextRow.push(nodes[i].right)
}
}
nodes = nextRow
nextRow = []
}
return result
}
treeToAry(ary2tree([1,2,null,3,4]))
[1, 2, null, 3, 4, null, null, null, null]
//空间复杂度不到n
function treeToAry2(root) {
if (!root) {
return []
}
var result = []
var i = 0
nodes = [root]
while (i < nodes.length) {
var node = nodes[i++]
if (nodes == null) {
result.push(null)
} else {
result.push(node.val)
nodes.push(node.left)
nodes.push(node.right)
}
}
return result
}
//空间复杂度2n
function treeToAry2(root) {
if (!root) {
return []
}
var result = []
nodes = [root]
while (nodes.length) {
var node = nodes.shift() //数组从前往后删除
if (nodes == null) {
result.push(null)
} else {
result.push(node.val)
nodes.push(node.left)
nodes.push(node.right)
}
}
return result
}
//按层
//直接处理下一行 按层次遍历
function treeToAry2(root) {
if (!root) {
return []
}
var result = [root.val]
var nodes = [root]
while (nodes.length) {
var node = nodes.shift()
if (node.left) {
nodes.push(node.left)
result.push(node.left.val)
} else {
result.push(null)
}
if (right.left) {
nodes.push(node.right)
result.push(node.right.val)
} else {
result.push(null)
}
while (result[result.length - 1] === null) {
result.pop()
}
return result
}
紧凑数组转化链式
function aryToTree(ary) {
if (ary.length == 0) {
return null
}
var root = createTreeNode(ary[0])
result = root
var queue = [root]
for (var i = 1; i < ary.length; i++) {
var node = queue.shift()
if (ary[i] !== null) {
node.left = createTreeNode(ary[i])
queue.push(node.left)
}
i++
if (i >= ary.length) {
break
}
if (ary[i] !== null) { //结点创建左右默认为null 不需要特意赋null
node.right = createTreeNode(ary[i])
queue.push(node.right)
}
}
return root
}
aryToTree([1, 2, null, 3, 4, null, null, 5, 6, null, 7])
二叉树的遍历
- 按层遍历
- 前序 root l r 中序 l root r 后序 l r root
function preOrderTraverse(root) {
if (root) {
console.log(root.val)
preOrderTraverse(root.left)
preOrderTraverse(root.right)
}
}
function inOrderTraverse(root) {
if (root) {
inOrderTraverse(root.left)
console.log(root.val)
inOrderTraverse(root.right)
}
}
function postOrderTraverse(root) {
if (root) {
postOrderTraverse(root.left)
postOrderTraverse(root.right)
console.log(root.val)
}
}
function preOrderTraverse(root, action) {
if (root) {
action(root.val)
preOrderTraverse(root.left, action)
preOrderTraverse(root.right, action)
}
}
function preOrder(root) {
var stack = []
while (true) {
while (root) {
console.log(root.val)
stack.push(root)
root = root.left
}
if (!stack.length) {
break
}
var node = stack.pop()
root = node.right
}
}
preOrder(aryToTree([1, 2, 3, 4, 5, 6, 7]))
function preOrder(root) {
var stack = []
while (true) {
if (root) {
console.log(root.val)
stack.push(root)
root = root.left
} else if (stack.length) {
root = stack.pop().right //stack非空 stack.pop一定非空
} else {
break
}
}
}
//空间复杂度 树的深度
自己搜索不用栈遍历二叉树
function inOrder(root) {
var stack = []
while (true) {
if (root) {
stack.push(root)
root = root.left
} else if (stack.length) {
root = stack.pop()
console.log(root.val)
root = root.right
} else {
break
}
}
}
inOrder(aryToTree([1, 2, 3, 4, 5, 6, 7]))
回顾
//[-1, -2, -3, 1, 2, null, 3, 4, null, null, 5]
//根节点从1开始 子节点的数学关系乱了
//n为偏移量 上例n=3 (i-n)*2+1+n/2 (2i - n + 1)
function ary2tree(ary, i = 0, n = i) {
if (ary[i] == null) {
return null
}
var root = createTreeNode(ary[i])
root.left = ary2tree(ary, 2 * i + 1 - n, n)
root.right = ary2tree(ary, 2 * i + 2 - n, n)
return root
}
function ary2tree5(ary, n) {
return ary2tree(ary, n)
function ary2tree(ary, i = 0) {
if (ary[i] == null) {
return null
}
var root = createTreeNode(ary[i])
root.left = ary2tree(ary, 2 * i + 1 - n)
root.right = ary2tree(ary, 2 * i + 2 - n)
return root
}
}
排序二叉树
- BST Binary Search Tree
- 左子树所有结点值都小于(或大于)根节点
- 右子树所有结点值都大于(或小于)根节点
- BST的中序遍历是升序的 反向中序遍历r c l 得到降序
- 应用乱序数组排序
function createTreeNode(val) {
return {
val: val,
left: null,
right: null
}
}
function insertIntoBST(bst, val) {
if (bst == null) {
return createTreeNode(val)
}
if (val < bst.val) {
bst.left = insertIntoBST(bst.left, val) //生成的新节点挂在树上
} else {
bst.right = insertIntoBST(bst.right, val)
}
return bst
}
function bstSort(ary) {
var tree = null
for (var i = 0; i < ary.length; i++) {
tree = insertIntoBST(tree, ary[i])
}//这里可以用reduce
var result = []
inOrderTraverse(tree, val => {
result.push(val)
})
return result
}
console.log(bstSort([8, 3, 4, 7, 2, 1, 5, 6, 9, 0]))
//时间复杂度n*logn 空间复杂度n 构造出来的bst占用的空间 理论最优
//平衡二叉树 所有结点左右子树深度差不超过1 最差n方
//非递归二叉搜索树
function insertIntoBST(bst, val) {
if (bst === null) {
return createTreeNode(val)
}
var p = bst
while (true) {
if (val < p.val) {
if (p.left) {
p = p.left
} else {
p.left = createTreeNode(val)
break
}
} else {
if (p.right) {
p = p.right
} else {
p.right = createTreeNode(val)
break
}
}
}
return bst
}
//空阿复杂度0(1)
判断数组是否升序
function isAscend(ary) {
ary[-1] = -Infinity
return ary.every((it, idx) => {
return it >= ary[idx - 1]
})
}
Tips
-
构建自平衡树 B + 红黑 AVL树
-
插入排序 n方
[2.1.4.7.5.8.9.0]
[1.2.4.7.5.8.9.0]
[1.2.4.5.7.8.9.0]
[1.2.4.5.7.8.9.0]
[1.2.4.5.7.8.0.9]
-
BST 高效查找 高效插入
-
排序的稳定性: 排序前后相同元素相对位置是否发生变化
[2, 1, 4, 7, 4, 8, 3, 6, 4, 7]
-
稳定:冒泡 BST 归并
不稳定:快读排序 选择排序(无序部分最小元素放入有序部分的最后)