概述
二叉树每个节点的子节点不允许超过2个。通过将子节点的个数限定为2,可以高效在树中插入、查找和删除数据。
二叉查找树(BST)是一种特殊的二叉树,相对较小的值保存在左节点中,较大的值保存在右节点中。这样的特性使得他的查找效率很高。
实现
定义BST类,有以下函数:
- insert 插入节点
- remove 删除节点
- isOrder 中序遍历
- preOrder 前序遍历
- postOrder 后序遍历
- getMin 查找最小值
- getMax 查找最大值
- find 查找指定值
function Node(data, left, right){
this.data = data
this.left = left
this.right = right
// 显示节点的数据
this.show = function(){
return this.data
}
}
// 二叉树查找树
function BST(){
this.root = null
// 添加节点
this.insert = function(data){
var n = new Node(data, null, null)
if(this.root == null){
this.root = n
}else{
// 设根节点为当前节点
var current = this.root, parent
while (true) {
parent = current
// 如果待插入节点数据小于当前节点,则设新的当前节点为原节点的左节点。否则设新的当前节点为原节点的右节点
if(data < current.data){
current = current.left
// 如果当前节点的左节点为null,就将新的节点插入这个位置,退出循环。
if(current == null){
parent.left = n
break
}
}else{
current = current.right
// 如果当前节点的右节点为null,就将新的节点插入这个位置,退出循环
if(current == null){
parent.right = n
break
}
}
}
}
}
// 删除节点
this.remove = function(data){
root = this.removeNode(this.root, data)
}
this.removeNode = function(node, data){
if(node == null) return null
if(data == node.data){
// 没有子节点
if(node.left == null && node.right == null) return null
// 没有左子节点
if(node.left == null) return node.right
// 没有右子节点
if(node.right == null) return node.left
// 有2个子节点
var tempNode = getSmallest(node.right)
node.data = tempNode.data
node.right = this.removeNode(node.right, tempNode.data)
return node
}else if(data < node.data){
node.left = this.removeNode(node.left, data)
}else{
node.right = this.removeNode(node.right, data)
}
}
// 中序遍历
this.isOrder = function(node){
if(node != null){
this.isOrder(node.left)
console.log(node.show()+ '')
this.isOrder(node.right)
}
}
// 前序遍历
this.preOrder = function(node){
if(node != null){
console.log(node.show() + ' ')
this.preOrder(node.left)
this.preOrder(node.right)
}
}
// 后序遍历
this.postOder = function(node){
if(node != null){
this.postOder(node.left)
this.postOder(node.right)
console.log(node.show() + ' ')
}
}
// 查找最小值,遍历左节点
this.getMin = function(){
var current = this.root
while (current.left != null) {
current = current.left
}
return current.data
}
// 查找最大值,遍历左节点
this.getMax = function(){
var current = this.root
while (current.right != null) {
current = current.right
}
return current.data
}
// 查找给定值
this.find = function(data){
var current = this.root
while (current != null) {
if(current.data == data){
return current
}else if(data < current.data){
current = current.left
}else{
current = current.right
}
}
return null
}
}
遍历
二叉树的遍历方式分为前序、中序、后序。
-
前序遍历:先访问根节点,然后以同样的方式访问左子树和右子树。
结果:23、16、3、22、45、37、99 -
中序遍历:以升序访问树中所有节点,先访问左子树 再访问根节点,最后访问右子树。
结果:3、16、22、23、37、45、99 -
后序遍历:先访问叶子节点,从左子树到右子树,再到根节点。
结果:3、22、16、37、99、45、23
力扣题型
二叉树翻转
二叉树翻转是指将左右节点的值进行交换。
解题思路:
- 定义临时变量存储节点的左子节点。
- 将节点的右子节点赋给节点的左子节点。
- 将临时变量存储节点的左子节点赋给节点的右子节点,完成交换。
- 递归遍历节点的左右子节点树。
function rolloverTree(node){
dfs(root)
function dfs(node){
if(!node) return
const temp = node.left
node.left = node.right
node.right = temp
dfs(node.left)
dfs(node.right)
}
return root
}
二叉树的后序遍历
按照访问左子树——右子树——根节点的方式遍历这棵树,而在访问左子树或者右子树的时候,我们按照同样的方式遍历,直到遍历完整棵树。
递归方式
function postOrderTraversal(root){
let res = new Array()
return postOrderTraversalNode(root, res)
}
function postOrderTraversalNode(node, res){
if(node){
postOrderTraversalNode(node.left, res)
postOrderTraversalNode(node.right, res)
res.push(node.val)
}
return res
}
迭代方式
function postOrderTraversal(root){
let res = []
if(!root) return res
let stack = [root]
while(stack.length){
root = stack.pop()
res.unshift(root.val)
if(root.left){
stack.push(root.left)
}
if(root.right){
stack.push(root.right)
}
}
return res
}
二叉树的深度
二叉树深度是指从树的根节点到最远的叶子节点需要经过的路径数。
解题思路:
- 将二叉树从根节点分为左、右2路向下进行计数。
- 到达叶节点目标并计数。
- 将左右叶节点计数进行比较。
- 将对比后较大的值返回。
function maxDepth(root){
var res = 1
dfs(root, res)
function dfs(node, level){
if(!node) return
if(level > res) res = level
dfs(node.left, level+1)
dfs(node.right, level+1)
}
return res
}
平衡二叉树
一颗空树或它的左右2个子树的高度差的绝对值不超过1,并且左右2个子树都是一颗平衡二叉树。
解题思路:
- 递归出口:当前树为空,那么这个树肯定是平衡二叉树。
- 判断左右子树的高度差,如果超过1那么就不是平衡二叉树。
- 分别递归左右子树。
function isBalanced(root)
if(!root) return true
if(Math.abs(getHeight(root.left)-getHeight(root.right))> 1){
return false
}
return isBalanced(root.left) && isBalanced(root.right)
}
function getHeight(node){
if(!node) return 0
return Math.max(getHeight(node.left), getHeight(node.right)) + 1
}
从前序与中序遍历序列构造二叉树
编写中...
完全二叉树的节点个数
编写中...
验证二叉树的前序序列化
序列化二叉树的一种方法是使用前序遍历。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如 #。
var isValidSerialization = function(preorder) {
var n = preorder.length
var stack = [1]
var i = 0
while(i < n){
if(!stack.length){
return false
}
if(preorder[i] === ','){
i++
}else if(preorder[i] === '#'){
stack[stack.length - 1]--
if(stack[stack.length - 1] === 0){
stack.pop()
}
i++
}else{
while(i < n && preorder[i] !== ','){
i++
}
stack[stack.length -1]--
if(stack[stack.length - 1] === 0){
stack.pop()
}
stack.push(2)
}
}
return !stack.length
}