数据结构-树

一. 树的概念

定义:树是一种非线性的数据结构。由n(n>=0)个节点组成.

1. 节点

它包含数据项,和指向其它节点的指针,上图中的树有7个节点。

2.节点的度

有几个分支,度就是几

3. 叶节点

度为0的节点被称为叶节点, 上图中C、D、F为叶节点

4.分支节点

除了叶节点之外的节点就是分支节点

5. 节点层次

节点所在的层级,如根节点A在第一层

6.树的深度

树中距离根节点最远的节点所处的层次就是树的深度,如上图为3

7. 树的度

树中节点的度的最大值。 上图中根节点的度最大,为3

8. 森林

森林是m(m>=0)颗数的集

 

二.二叉树

定义:二叉树是一种特殊情况,每个节点最多有两个子女,分别为左子女和右子女

二叉树的性质:

a. 二叉树的第i(i>=1)层,最多存在2的i-1次方个节点。

b.深度为k(k>=0)的二叉树,最少有K个节点,最多有2的k-1次方个节点。

c. 对于一个非空二叉树,叶节点的数量等于度为2的节点数量加1。

特殊二叉树:

满二叉树: 深度为k的满二叉树,是有2的k-1次方个节点的二叉树,每一层都达到了可以容纳的最大数量的节点

完全二叉树: 深度为k的完全二叉树,从第一层到第k-1层都是满的,第k层,或是满的,或是从右向左连续缺若干个节点

 

三.code定义二叉树

// 定义二叉树节点
class BinTreeNode {
    constructor(data) {
        this.data = data
        this.parentNode = null //父节点
        this.leftChild = null  //左节点
        this.rightChild = null //右节点
    }
}

遍历广义表字符串,来建立一颗二叉树。

广义表A(B(D,E(G,)),C(,F))#: 广义表的表名放在表前,表示树的根节点,括号中的是根的左右子树,整个广义表的最后加上特殊符号#表示输入结束

实现思路:

*遇到左括号的时候,说明前面有一个节点,这个括号里的两个节点都是它的子节点,但是子节点后面还会有子节点,因此,我们需要一个先进后出的数据结构把前面的节点保存下来,这样栈顶就是当前要处理的两个节点的父节点;

*逗号分隔了左右子树,因此需要一个变量来标识遇到的是左子树还是右子树,假设这个变量为k,遇到左括号的时候,k=1,表示开始识别左子树;

*遇到逗号,k=2表示开始识别右子树;

*遇到右括号,说明一棵子树结束了,那么栈顶的元素就是这颗子树的根节点,执行pop方法出栈

class BinTree {
    constructor () {
        this.root = null  //根节点
    }

    /**
     * 初始化二叉树
     */
    initTree (str) {
        if (!str) return 
        var flag = 0
        let newNode = null
        let stack = new Stack()
        for (let i = 0; i < str.length; i++) {
            // 标记左右子树
            let item = str[i]
            // # 结束标志
            if (item === '#') {
                break
            }

            if (item === '(') {
                flag = 1
                stack.push(newNode)
            } else if (item === ',') {
                flag = 2
            } else if (item === ')') {
                stack.pop()
            } else {
                // 创建新节点
                newNode = new BinTreeNode(item)
                if (!this.root) {
                    this.root = newNode
                } else {
                    // k为1添加左节点,2添加右节点
                    if (flag === 1) {
                        let topNode = stack.top()
                        topNode.leftChild = newNode
                        newNode.parentNode = topNode
                    } else if (flag === 2) {
                        let topNode = stack.top()
                        topNode.rightChild = newNode
                        newNode.parentNode = topNode
                    }
                }
            }
        }
    }

    /**
     * 树的根节点
     */
    getRoot () {
        return this.root
    }

    getTreeCount (node) {
        if (!node) return 0
        // 返回的是左孩子节点和有孩子节点再加上当前节点个数
        let leftSize = this.getTreeCount(node.leftChild)
        let rightSize = this.getTreeCount(node.rightChild)

        return leftSize + rightSize + 1
    }

    /**
     * 树的节点个数
     */
    size () {
        return this.getTreeCount(this.root)
    }

    getTreeHeight (node) {
        if (!node) return 0
        // 先计算左右节点的深度
        let leftHeight = this.getTreeHeight(node.leftChild)
        let rightHeight = this.getTreeHeight(node.rightChild)
        // 返回较大者
        if (leftHeight > rightHeight) {
            return leftHeight + 1
        } else {
            return rightHeight + 1
        }
    }

    /**
     * 树的高度(深度)
     */
    height () {
        this.getTreeHeight(this.root)
    }

    findTreeNode (node, data) {
        if (!node) return null

        if (node.data === data) return node

        // 先找左节点
        let leftNode = this.findTreeNode(node.leftChild)
        if (leftNode) {
            return leftNode
        }

        // 没找到,从右节点找
        return this.findTreeNode(node.rightHeight)
    }
    /**
     * 查找节点
     */
    findNode (data) {
        this.findTreeNode(this.root, data)
    }

    /**
     * 前序遍历(当前节点->当前节点的左节点->当前节点的右节点)
     */
    preOrder (node) {
        if (!node) return

        console.log(node.data)
        this.preOrder(node.leftChild)
        this.preOrder(node.rightChild)
    }

    /**
     * 中序遍历(当前节点的左节点->当前节点->当前节点的右节点)
     */
    inOrder (node) {
        if (!node) return

        this.inOrder(node.leftChild)
        console.log(node.data)
        this.inOrder(node.rightChild)
    }

    /**
     * 后序遍历(当前节点的左节点->当前节点的右节点->当前节点)
     */
    postOrder (node) {
        if (!node) return
        this.postOrder(node.leftChild)
        this.postOrder(node.rightChild)
        console.log(node.data)
    }
}

定义栈:

class Stack {
    constructor() {
       this.items = []
    }

    push (data) {
        this.items.push(data)
    }

    pop () {
        let popItem = this.items.pop()
        return popItem
    }

    top () {
        return this.items[this.items.length - 1]
    }

    size () {
        return this.items.length
    }

    clear () {
        this.items = []
    }

    isEmpty () {
        return this.items.length === 0
    }
}

测试代码:

let binTree = new BinTree()
binTree.initTree("A(B(D,E(G,)),C(,F))#")
binTree.preOrder(binTree.root)   // A B D E G C F
console.log(binTree.size())

扩展:

1. 树的镜像:对于一棵树,如果每个节点的左右子树互换位置,那么就变成了这颗树的镜像。

function mirror (node) {
    if (!node) return 
    let temp = node.leftChild
    node.leftChild = node.rightChild
    node.rightChild = temp
    mirror(node.leftChild)
    mirror(node.rightChild)
}

mirror(binTree.root)
binTree.inOrder(binTree.root) // F C A E G B D

2. 前序遍历的非递归实现 

function preOrder(node){
    var stack = new Stack()
    var curr_node = node
    while (curr_node) {
        console.log(curr_node.data)
        if (curr_node.rightChild) {
            stack.push(curr_node.rightChild)
        }
        if (curr_node.leftChild) {
            curr_node = curr_node.leftChild
        } else {
            //没有左子树
            curr_node = stack.pop()
        }
    }
}
preOrder(binTree.root)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值