zoukankan      html  css  js  c++  java
  • JS中数据结构之二叉查找树

    树是一种非线性的数据结构,以分层的方式存储数据。在二叉树上进行查找非常快,为二叉树添加或删除元素也非常快。

    一棵树最上面的节点称为根节点,如果一个节点下面连接多个节点,那么该节点称为父节点,它下面的节点称为子节点。一个节点可以有 0 个、1 个或多个子节点,没有任何子节点的节点称为叶子节点。

    二叉树是一种特殊的树,它的子节点个数不超过两个,一个父节点的两个子节点分别称为左节点和右节点。树中任何一层的节点可以都看做是子树的根,树的层数就是树的深度。

    二叉查找树是一种特殊的二叉树,相对较小的值保存在左节点中,较大的值保存在右节点中。

    二叉查找树的实现

    node 类定义节点

    function Node(data, left, right) {
      this.data = data;
      this.left = left;
      this.right = right;
      this.show = show;
    }

    show() 方法展示节点数据

    function show() {
      return this.data;
    }

    创建 BST 类表示二叉查找树。该类的构造函数将根节点初始化为 null

    function BST() {
      this.root = null;
      this.insert = insert;
      this.inOrder = inOrder;
      this.preOrder = perOrder;
      this.postOrder = postOrder;
      this.update = update;
      this.getMin = getMin;
      this.getMax = getMax;
      this.find = find;
      this.remove = remove;
    }

    insert() 方法向树中加入新节点。首先创建一个 Node 对象,将数据传入该对象保存。 其次检查 BST 是否有根节点,如果没有,那么该节点就是根节点;否则,就需要准备遍历 BST,找到插入的适当位置。

    查找正确的插入位置的算法逻辑:

    (1) 设根节点为当前节点。

    (2) 如果待插入节点保存的数据小于当前节点,则设新的当前节点为原节点的左节点;反之,执行第 4 步。

    (3) 如果当前节点的左节点为 null,就将新的节点插入这个位置,退出循环;反之,继续执行下一次循环。

    (4) 设新的当前节点为原节点的右节点。

    (5) 如果当前节点的右节点为 null,就将新的节点插入这个位置,退出循环;反之,继续执行下一次循环。

    function insert(data) {
      var n = new Node(data, null, null);
      if (this.root == null) {
        this.root = n;
      } else {
        var current = this.root;
        var parent;
        while (true) {
          parent = current;
          if (data < current.data) {
            current = current.left;
            if (current == null) {
              parent.left = n;
              break;
            }
          } else {
            current = current.right;
            if (current == null) {
              parent.right = n;
              break;
            }
          }
        }
      }
    }

    遍历二叉查找树

    ① NLR:先序遍历
    ——访问根结点的操作发生在遍历其左右子树之前。
    ② LNR:中序遍历
    ——访问根结点的操作发生在遍历其左右子树之中(间)。
    ③ LRN:后序遍历
    ——访问根结点的操作发生在遍历其左右子树之后。
    中序遍历以升序访问树中所有节点,先访问左子树,再访问根节点,最后访问右子树
    function inOrder(node) {
      if (!(node == null)) {
        inOrder(node.left);
        console.log(node.show() + " ");
        inOrder(node.right);
      }
    }  //上图中序遍历顺序: 3 16 22 23 37 45 99

    先序遍历

    function preOrder(node) {
      if (!(node == null)) {
        putstr(node.show() + " ");
        preOrder(node.left);
        preOrder(node.right);
      }
    }  //上图先序遍历顺序:23 16 3 22 45 37 99

    后序遍历

    function postOrder(node) {
      if (!(node == null)) {
        postOrder(node.left);
        postOrder(node.right);
        putstr(node.show() + " ");
      }
    }  //上图后序遍历顺序:3 22 16 37 99 45 23

    二叉树上查找最小值和最大值

    function getMin() {  //遍历左子树,直到找到最后一个节点
      var current = this.root;
      while (!(current.left == null)) {
        current = current.left;
      }
      return current.data;
    }
    
    function getMax() {  //遍历右子树,直到找到最后一个节点,
      var current = this.root;
      while (!(current.right == null)) {
        current = current.right;
      }
      return current.data;
    }

    find() 方法查找给定值,如果找到就返回保存该值的节点;如果没找到,该方法返回 null

    function find(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;
    }

    从二叉查找树上删除节点

    BST 上删除节点是最复杂的操作。如果删除没有子节点的节点,那么非常简单;如果节点只有一个子节点就比较复杂;而删除包含两个子节点的节点最复杂。

    从 BST 中删除节点的第一步是判断当前节点是否是待删除的数据,如果是,则删除该节点;否则就比较当前节点上的数据和待删除的数据。如果待删除数据小于当前节点上的数据,则移至当前节点的左子节点继续比较;如果删除数据大于当前节点上的数 据,则移至当前节点的右子节点继续比较。

    如果待删除节点是叶子节点,那么只需要将从父节点指向它的链接指向 null。 如果待删除节点只包含一个子节点,那么就让原本指向它的节点指向它的子节点。 最后,如果待删除节点包含两个子节点,正确的做法有两种:要么查找待删除节点左子树上的最大值,要么查找其右子树上的最小值。下面的函数选择的是后一种方式。

    整个删除过程由两个方法完成。remove() 方法只是简单地接受待删除数据,调用 removeNode() 删除它,removeNode()方法才是完成主要工作的方法。

    function remove(data) {
      root = removeNode(this.root, data);
    }
    
    function removeNode(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;
        }
        // 有两个子节点的节点
        var tempNode = getSmallest(node.right);  //查找右子树上的最小值
        node.data = tempNode.data;    //将最小值复制到待删除节点
        node.right = removeNode(node.right, tempNode.data);  //最后删除最小值节点
        return node;
      } else if (data < node.data) {
        node.left = removeNode(node.left, data);
        return node;
      } else {
        node.right = removeNode(node.right, data);
        return node;
      }
    }
    
    function getSmallest(node){
      while(node.left != null) {
        node = node.left;
      }
      return node;
    }

    计数:记录在一组数据集中数据出现的次数。如果该数据尚未在 BST 中出现,就将其加入 BST;如果已经出现,就将出现的次数加 1。

    修改 Node 对象,为其增加一个记录数据出现频次的成员

    function Node(data, left, right) {
      this.data = data;
      this.count = 1;
      this.left = left;
      this.right = right;
      this.show = show;
    }

    update() 方法当数据已经存在二叉树中时更新数据出现的频次

    function update(data) {
      var grade = this.find(data);  //当找到数据即返回含有该数据的节点,当没找到数据时(即数据不存在当前二叉树中)返回null
      grade.count++;
      return grade;
    }
  • 相关阅读:
    char类型到底是有符号还是无符号
    GNU C编译器的gnu11和c11
    kotlin之包
    mysql 各个版本驱动jar包
    网络优化
    Android 布局优化
    Android之MVVM开发模式
    Android蓝牙开发技术学习总结
    Android 电量优化
    Android中图片优化
  • 原文地址:https://www.cnblogs.com/wenxuehai/p/10302903.html
Copyright © 2011-2022 走看看