学习javascript 数据结构 之 二叉树

154 阅读2分钟

概述

二叉树每个节点的子节点不允许超过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
    }
}

遍历

image.png 二叉树的遍历方式分为前序、中序、后序。

  1. 前序遍历:先访问根节点,然后以同样的方式访问左子树和右子树。

    结果:23、16、3、22、45、37、99

  2. 中序遍历:以升序访问树中所有节点,先访问左子树 再访问根节点,最后访问右子树。

    结果:3、16、22、23、37、45、99

  3. 后序遍历:先访问叶子节点,从左子树到右子树,再到根节点。

    结果:3、22、16、37、99、45、23

力扣题型

二叉树翻转

二叉树翻转是指将左右节点的值进行交换。

解题思路:

  1. 定义临时变量存储节点的左子节点。
  2. 将节点的右子节点赋给节点的左子节点。
  3. 将临时变量存储节点的左子节点赋给节点的右子节点,完成交换。
  4. 递归遍历节点的左右子节点树。
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
}

二叉树的深度

二叉树深度是指从树的根节点到最远的叶子节点需要经过的路径数。

解题思路:

  1. 将二叉树从根节点分为左、右2路向下进行计数。
  2. 到达叶节点目标并计数。
  3. 将左右叶节点计数进行比较。
  4. 将对比后较大的值返回。
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. 递归出口:当前树为空,那么这个树肯定是平衡二叉树。
  2. 判断左右子树的高度差,如果超过1那么就不是平衡二叉树。
  3. 分别递归左右子树。
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
}

二叉树的第K大节点

树的子结构

监控二叉树

二叉树最大宽度