树结构

树结构示意图

 

树的术语

树(tree):n个节点构成的有限集合,当n=0时称为空树,对于任一非空树,具有一个根节点用r表示,其余节点可分为m(m>0)个互不相交的有限集T1,T2,T3等,其中每一个集合本身又是一棵树,称为原来树的子树

 

节点的度(Degree):节点的子树个数

树的度:树的所有节点中最大的度数

叶节点(Leaf):度为0的节点(也称为叶子节点)

父节点(Parent):有子树的节点是其子树的根节点的父节点

子节点(Child):若A节点是B节点的父节点,则称B节点是A节点的子节点;子节点也称为孩子节点

兄弟节点(Sibling):具有同一父节点的各节点彼此是兄弟节点

路径和路径长度:从节点n1到nk的路径为一个节点序列n1,n2......nk,ni是ni+1的父节点,路径包含边的个数为路径的长度

节点的层次(Level):规定根节点在1层,其他任一节点的层数是其父节点的层数加一

树的深度(Depth):树中所有节点中的最大层次是这棵树的深度

树的表示方法

 

 

二叉树 

如果树中每个节点最多只能有两个子节点,这样的树就称为二叉树

二叉树的定义:

  1、二叉树可以为空,也就是没有节点

  2、若不为空,则它是由根节点和称为其左子树TL和右子树TR的两个互不相交的二叉树组成

二叉树有五种形态:

 

 二叉树的特性:

一个二叉树第i层的最大节点树为:2^(i-1),i>=1

深度为k的二叉树有最大节点总数为:2^k-1,k>=1

对任何非空二叉树T,若n0表示叶节点的个数,n2是度为2的非叶节点个数,那么两者满足关系n0=n2+1

完美二叉树

在二叉树中,除了最下一层的叶节点外,每层节点都有2个子节点,就构成了满二叉树

 

 完全二叉树

除二叉树最后一层外,其他各层的节点数都达到最大个数,且最后一层从左向右的叶节点连续存在,只缺右侧若干节点,完美二叉树是特殊的完全二叉树

 

 

二叉树的存储

二叉树常见的存储方式是数组和链表

使用数组

完全二叉树:按从上至下、从左至右顺序存储

 

 非完全二叉树:非完全二叉树要转成完全二叉树才可以按照上面的方案存储,但是会造成很大的空间浪费

 

 二叉树最常见的方式还是使用链表存储

每个节点封装成一个Node,Node中包含存储的数据,左节点的引用,右节点的引用

 

二叉搜索树

二叉搜索树(BST,Binary Search Tree)也称二叉排序树或者二叉查找树

二叉搜索树是一颗二叉树,可以空

如果不为空,满足一下性质:

  • 非空左子树的所有键值小于其根节点的键值
  • 非空右子树的所有键值大于其根节点的键值
  • 左、右子树本身也都是二叉搜索树

二叉搜索树的特点:

二叉搜索树的特点就是相对较小的值总是保存在左节点上,相对较大的值总是保存在右节点上,二叉搜索树的查找效率特别高

二叉搜索树常见的操作方法

<1>  insert(key):向树中插入一个新的键

<2>  search(key):在树中查找一个键,如果节点存在,则返回true,如果不存在,则返回false

<3>  inOrderTraverse:通过中序遍历方式遍历所有节点

<4>  preOrderTraverse:通过先序遍历方式遍历所有节点

<5>  postOrderTraverse:通过后序遍历方式遍历所有节点

<6>  min:返回树中最小的值/键

<7>  max:返回树中最大的值/键

<8>  remove(key):从树中移除某个键

封装二叉搜索树

  //封装二叉搜索树
    function BinarySearchTree() {
        function Node(key) {
            this.key = key
            this.left = null
            this.right = null
        }
        this.root = null
    }

 

insert方法

//封装二叉搜索树
    function BinarySearchTree() {
        function Node(key) {
            this.key = key
            this.left = null
            this.right = null
        }
        this.root = null
        BinarySearchTree.prototype.insert = function (key) {
            //!、根据key创建节点
            var newNode = new Node(key)
            //2、判断是否存在根节点
            if (this.root == null) {
                this.root = newNode
            } else {
                this.insertNode(this.root, newNode)
            }
        }
        //判断节点大小的递归方法
        BinarySearchTree.prototype.insertNode = function (node, newNode) {
            if (newNode.key < node.key) {
                //如果新节点key小于根节点的key就向左查找
                if (node.left == null) {
                    //没有左节点,就放在这里当做左节点
                    node.left = newNode
                } else {
                    //如果有左节点,递归调用
                    this.insertNode(node.left, newNode)
                }
            } else {
                //如果新节点key大于根节点的key就向右查找
                if (node.right == null) {
                    //没有右节点,就放在这里当做右节点
                    node.right = newNode
                } else {
                    //如果有右节点,递归调用
                    this.insertNode(node.right, newNode)
                }
            }
        }
    }

测试:

    var tree = new BinarySearchTree()
    tree.insert(11)
    tree.insert(7)
    tree.insert(15)
    tree.insert(5)
    tree.insert(3)
    tree.insert(9)
    tree.insert(8)
    tree.insert(10)
    tree.insert(13)
    tree.insert(12)
    tree.insert(14)
    tree.insert(20)
    tree.insert(18)
    tree.insert(25)

 

二叉树的遍历

二叉树遍历常见的有三种方式:先序遍历、中序遍历、后序遍历、层序遍历(使用较少)

 先序遍历

先访问根节点,然后再访问左子节点,最后访问右子节点

       //遍历
        BinarySearchTree.prototype.preOrderTraversal = function (handler) {
            this.preOrderTraversalNode(this.root, handler)
        }
        BinarySearchTree.prototype.preOrderTraversalNode = function (node, handler) {
            if(node != null) {
                //处理经过的节点
                handler(node.key)
                //处理经过节点的左子节点
                this.preOrderTraversalNode(node.left,handler)
                //处理经过节点的右子节点
                this.preOrderTraversalNode(node.right,handler)
            }
        }

测试:

 var tree = new BinarySearchTree();
    tree.insert(11)
    tree.insert(7)
    tree.insert(15)
    tree.insert(5)
    tree.insert(3)
    tree.insert(9)
    tree.insert(8)
    tree.insert(10)
    tree.insert(13)
    tree.insert(12)
    tree.insert(14)
    tree.insert(20)
    tree.insert(18)
    tree.insert(25)
    var result = ''
    tree.preOrderTraversal(function (key) {
        result += key + ' '
    })
    alert(result)

中序遍历

先访问左子节点,然后再访问根节点,最后访问右子节点

BinarySearchTree.prototype.midOrderTraversal = function (handler) {
            this.midOrderTraversalNode(this.root, handler)
        }
        BinarySearchTree.prototype.midOrderTraversalNode = function (node,handler) {
            if(node != null) {
                //处理经过的节点的左子节点
                this.midOrderTraversalNode(node.left,handler)
                //处理经过的节点
                handler(node.key)
                //处理经过节点的右子节点
                this.midOrderTraversalNode(node.right,handler)
            }
        }

测试:

 var tree = new BinarySearchTree();
    tree.insert(11)
    tree.insert(7)
    tree.insert(15)
    tree.insert(5)
    tree.insert(3)
    tree.insert(9)
    tree.insert(8)
    tree.insert(10)
    tree.insert(13)
    tree.insert(12)
    tree.insert(14)
    tree.insert(20)
    tree.insert(18)
    tree.insert(25)
    var result = ''
    tree.midOrderTraversal(function (key) {
        result += key + ' '
    })
    alert(result)

 

 后序遍历

先访问左子节点,然后再访问右子节点,最后访问根节点

  BinarySearchTree.prototype.postOrderTraversal = function (handler) {
            this.postOrderTraversalNode(this.root, handler)
        }
        BinarySearchTree.prototype.postOrderTraversalNode = function (node,handler) {
            if(node != null) {
                //处理经过的节点的左子节点
                this.postOrderTraversalNode(node.left,handler)
                //处理经过节点的右子节点
                this.postOrderTraversalNode(node.right,handler)
                //处理经过的节点
                handler(node.key)
            }
        }

测试:

 var tree = new BinarySearchTree();
    tree.insert(11)
    tree.insert(7)
    tree.insert(15)
    tree.insert(5)
    tree.insert(3)
    tree.insert(9)
    tree.insert(8)
    tree.insert(10)
    tree.insert(13)
    tree.insert(12)
    tree.insert(14)
    tree.insert(20)
    tree.insert(18)
    tree.insert(25)
    var result = ''
    tree.postOrderTraversal(function (key) {
        result += key + ' '
    })
    alert(result)

 

 

获取最大值最小值

 BinarySearchTree.prototype.max = function () {
            //获取根节点
            var node = this.root
            //依次向右查找,直到节点为null
            var key = null
            while (node != null) {
                key = node.key
                node = node.right
            }
            return key
        }
        BinarySearchTree.prototype.min = function () {
            //获取根节点
            var node = this.root
            var key = null
            //依次向左查找,直到节点为null
            while (node != null) {
                key = node.key
                node = node.left
            }
            return key
        }

测试

var tree = new BinarySearchTree();
    tree.insert(11)
    tree.insert(7)
    tree.insert(15)
    tree.insert(5)
    tree.insert(3)
    tree.insert(9)
    tree.insert(8)
    tree.insert(10)
    tree.insert(13)
    tree.insert(12)
    tree.insert(14)
    tree.insert(20)
    tree.insert(18)
    tree.insert(25)
    alert(tree.max())
    alert(tree.min())

 

 搜索特定的值

 BinarySearchTree.prototype.search = function (key) {
            //获取根节点
            var node = this.root
            //循环搜索key
            while (node != null) {
                if (key < node.key) {
                    node = node.left
                } else if (key > node.key) {
                    node = node.right
                } else {
                    return true
                }
            }
            return false
        }

测试:

   var tree = new BinarySearchTree();
    tree.insert(11)
    tree.insert(7)
    tree.insert(15)
    tree.insert(5)
    tree.insert(3)
    tree.insert(9)
    tree.insert(8)
    tree.insert(10)
    tree.insert(13)
    tree.insert(12)
    tree.insert(14)
    tree.insert(20)
    tree.insert(18)
    tree.insert(25)
    alert(tree.search(25))
    alert(tree.search(2))

 

 

删除操作

 思路:

1、找到要删除的节点,如果没有找到,不需要删除

2、找到要删除的节点

  1)删除叶子节点

  2)删除只有一个子节点的节点

  3)删除有两个子节点的节点

  BinarySearchTree.prototype.remove = function (key) {
            //1、寻找删除的节点
            //定义变量,保存一些信息
            var current = this.root
            var parent = null
            var isLeftChild = true
            //寻找删除的节点
            while (current.key != key) {
                parent = current
                if (key < current.key) {
                    isLeftChild = true
                    current = current.left
                } else {
                    isLeftChild = false
                    current = current.right
                }
                //没有找到,已经找到了最后的节点仍然没有找到
                if (current == null) return false
            }
            //2、根据情况删除节点
            //删除的节点是叶子节点
            if (current.left == null && current.right == null) {
                if (current == this.root) {
                    this.root = null
                } else if (isLeftChild) {
                    parent.left = null
                } else {
                    parent.right = null
                }
            }
            // 删除的节点有一个子节点
            else if (current.right == null) {
                if (current == this.root) {
                    this.root = current.left
                } else if (isLeftChild) {
                    parent.left = current.left
                } else {
                    parent.right = current.left
                }
            } else if (current.left == null) {
                if (current == this.root) {
                    this.root = current.right
                } else if (isLeftChild) {
                    parent.left = current.right
                } else {
                    parent.right = current.right
                }
            }
            // 删除的节点有两个子节点
            else {
                //获取后继节点
                var successor = this.getSuccessor(current)
                //判断是否根节点
                if(current == this.root) {
                    this.root = successor
                }else if(isLeftChild) {
                    parent.left = successor
                } else {
                    parent.right = successor
                }
            successor.left = current.left
            }
        }
        //找后继节点的方法
        BinarySearchTree.prototype.getSuccessor = function (delNode) {
            //定义变量
            var successor = delNode
            var current = delNode.right
            var successorParent = delNode
            //循环查找
            while(current!=null){
                successorParent = successor
                successor = current
                current = current.left
            }
            //判断寻找的后继节点是否直接就是delNode的right节点
            if(successor!=delNode.right){
                successorParent.left = successor.right
                successor.right = delNode.right
            }
            return successor
        }

测试:

 var tree = new BinarySearchTree();
    tree.insert(11)
    tree.insert(7)
    tree.insert(15)
    tree.insert(5)
    tree.insert(3)
    tree.insert(9)
    tree.insert(8)
    tree.insert(10)
    tree.insert(13)
    tree.insert(12)
    tree.insert(14)
    tree.insert(20)
    tree.insert(18)
    tree.insert(25)
    tree.remove(9)
    tree.remove(7)
    tree.remove(15)
    var result = ''
    tree.postOrderTraversal(function (key) {
        result += key + ' '
    })
    alert(result)

 

 

posted @ 2022-01-16 16:44  keyeking  阅读(1015)  评论(0编辑  收藏  举报