JS中的算法与数据结构之二叉查找树(Binary Sort Tree)实例详解

本文实例讲述了JS中的算法与数据结构之二叉查找树(Binary Sort Tree)。分享给大家供大家参考,具体如下:

二叉查找树(Binary Sort Tree)

我们之前所学到的列表,栈等都是一种线性的数据结构,今天我们将学习计算机中经常用到的一种非线性的数据结构——树(Tree),由于其存储的所有元素之间具有明显的层次特性,因此常被用来存储具有层级关系的数据,比如文件系统中的文件;也会被用来存储有序列表等。

在树结构中,每一个结点只有一个前件,称为父结点,没有前件的结点只有一个,称为树的根结点,简称树的(root)。每一个结点可以有多个后件,称为该结点的子结点。没有后件的结点称为叶子结点。一个结点所拥有的子结点的个数称为该结点的度,所有结点中最大的度称为树的度。树的最大层次称为树的深度。

二叉树

二叉树是一种特殊的树,它的子节点个数不超过两个,且分别称为该结点的左子树(left subtree)与右子树(right subtree),二叉树常被用作二叉查找树和二叉堆或是二叉排序树(BST)。

 
二叉树

按一定的规则和顺序走遍二叉树的所有结点,使每一个结点都被访问一次,而且只被访问一次,这个操作被称为树的遍历,是对树的一种最基本的运算。由于二叉树是非线性结构,因此,树的遍历实质上是将二叉树的各个结点转换成为一个线性序列来表示。

按照根节点访问的顺序不同,树的遍历分为以下三种:前序遍历,中序遍历,后序遍历;

前序遍历:根节点->左子树->右子树

 
先序遍历

中序遍历:左子树->根节点->右子树

 
中序遍历

后序遍历:左子树->右子树->根节点

 
后序遍历

因此我们可以得之上面二叉树的遍历结果如下:前序遍历:ABDEFGC 中序遍历:DEBGFAC 后序遍历:EDGFBCA

二叉查找树(BST)

实际应用中,树的每个节点都会有一个与之相关的值对应,有时候会被称为。因此,我们在构建二叉查找树的时候,确定子节点非常的重要,通常将相对较小的值保存在左节点中,较大的值保存在右节点中,这就使得查找的效率非常高,因此被广泛使用。

二叉查找树的实现

根据上面的知识,我们了解到二叉树实际上是由多个节点组成,因此我们首先就要定义一个Node类,用于存放树的节点,其构造与前面的链表类似。Node类的定义如下:

//节点定义

function Node (data , left , right) {
  this.data = data;    // 数据
  this.left = left;    // 左节点
  this.right = right;   // 右节点
  this.show = show;    // 显示节点数据
}

function show(){
  return this.data;
}

Node对象既保存了数据,也保存了它的左节点和右节点的链接,其中 show 方法用来显示当前保存在节点中数据。

现在我们可以创建一个类,用来表示二叉查找数(BST),我们初始化类只包含一个成员,一个表示二叉查找树根节点的 Node 对象,初始化为 null , 表示创建一个空节点。

//二叉查找树(BST)的类

function BST(){
  this.root = null;      // 根节点
  this.insert = insert;    // 插入节点
  this.preOrder = preOrder;  // 先序遍历
  this.inOrder = inOrder;   // 中序遍历
  this.postOrder = postOrder; // 后序遍历
  this.find = find;      // 查找节点
  this.getMin = getMin;    // 查找最小值
  this.getMax = getMax;    // 查找最大值
  this.remove = remove;    // 删除节点
}

现在,我们需要为我们的类添加方法。

首先就是 insert 方法,向树中添加一个新节点,我们一起来看看这个方法;

insert:向树中添加新节点

因为添加节点会涉及到插入位置的问题,必须将其放到正确的位置上,才能保证树的正确性,整个过程较为复杂,我们一起来梳理一下:

首先要添加新的节点,首先需要创建一个Node对象,将数据传入该对象。

其次要检查当前的BST树是否有根节点,如果没有,那么表示是一棵新数,该节点就为该树的根节点,那么插入这个过程就结束了;否则,就要继续进行下一步了。

如果待插入节点不是根节点,那么就必须对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;
        }
      }
    }
  }
}

现在BST类已初步成型,但操作还仅仅限于插入节点,我们需要有遍历BST的能力,上面我们也提到了是三种遍历方式。其中中序遍历是最容易实现的,我们需要升序的方法访问树中的所有节点,先访问左子树,在访问根节点,最后是右子树,我们采用递归来实现!

inOrder:中序遍历

 // 中序遍历

function inOrder (node) {
  if( !(node == null )){
    inOrder( node.left );
    console.debug( node.show() + ' ');
    inOrder( node.right );
  }
}

怎么样,了解了原理,实现起来还是蛮简单的~

我们用一段代码来测试一下我们所写的中序遍历:

var nums = new BST();
//插入数据
nums.insert(23);
nums.insert(45);
nums.insert(16);
nums.insert(37);
nums.insert(3);
nums.insert(99);
nums.insert(22);

上述插入数据后,会形成如下的二叉树

 
BST

中序遍历结果如下:

//中序遍历
console.log("Inorder traversal: ");
inOrder(nums.root);

// Inorder traversal:
// 3 16 22 23 37 45 99

preOrder:先序遍历

有了中序遍历的基础,相信先序遍历的实现你已经想出来,怎么样?看看对吗?

//先序遍历

function preOrder( node ) {
  if( !(node == null )){
    console.log( node.show() + ' ');
    preOrder( node.left );
    preOrder( node.right );
  }
}

怎么样,看起来是不是和中序遍历差不多,唯一的区别就是 if 语句中代码的执行顺序,中序遍历中 show 方法放在两个递归调用之间,先序遍历则放在递归调用之前。

先序遍历结果如下:

// 先序遍历

console.log("Preorder traversal: ");
preOrder(nums.root);

// Preorder traversal:
// 23 16 3 22 45 37 99

postOrder:后序遍历

后序遍历的实现和前面的基本相同,将 show 方法放在递归调用之后执行即可

//后序遍历

function postOrder ( node ) {
  if( !(node == null ) ){
    postOrder( node.left );
    postOrder( node.right );
    console.log( node.show() + ' ');
  }
}

后序遍历结果如下:

// 后序遍历

console.log("Postorder traversal: ");
postOrder(nums.root);

// Postorder traversal:
// 3 22 16 37 99 45 23

二叉查找树的查找运算

对于BST通常有一下三种的查找类型:

  1. 查找给定值
  2. 查找最大值
  3. 查找最小值

我们接下来一起来讨论三种查找的方式的实现。

要查找BST中的最小值和最大值是非常简单的。因为较小的值总是在左子节点上,要想查找BST中的最小值,只需遍历左子树,直到找到最后一个节点即可。同理,查找最大值,只需遍历右子树,直到找到最后一个节点即可。

getMin:查找最小值

遍历左子树,直到左子树的某个节点的 left 为 null 时,该节点保存的即为最小值

//查找最小值

function getMin( ) {
  var current = this.root;
  while ( !( current.left == null ) ){
    current = current.left;
  }
  return current.show();
}

getMax:查找最大值

遍历右子树,直到右子树的某个节点的 right 为 null 时,该节点保存的即为最大值

//查找最大值

function getMax () {
  var current = this.root;
  while ( !( current.right == null ) ) {
    current = current.right;
  }
  return current.show();
}

我们还是利用前面构建的树来测试:

// 最小值
console.log('min:' + nums.getMin() );    // min : 3

//最大值
console.log('max:' + nums.getMax() );    // max : 99

在BST上查找给定值,需要比较给定值和当前节点保存的值的大小,通过比较,就能确定给定值在不在当前节点,根据BST的特点,qu接下来是向左还是向右遍历;

//查找给定值

function find ( data ) {
  var current = this.root;
  while ( current != null ){
    if( current.data == data ){
      return current;
    }else if( current.data < data ){
      current = current.right;
    }else{
      current = current.left;
    }
  }
  return null;
}

如果找到给定值,该方法返回保存该值的节点,反之返回null;

//查找不存在的值
console.log('find:' + nums.find(66));    // find : null

//查找存在的值
console.log('find:' + nums.find(99) );   // find : [object Object]

二叉查找树的删除运算

从BST中删除节点的操作最为复杂,其复杂程度取决于删除的节点位置。如果待删除的节点没有子节点,那么非常简单。如果删除包含左子节点或者右子节点,就变得稍微有些复杂。如果删除包含两个节点的节点最为复杂。

我们采用递归方法,来完成复杂的删除操作,我们定义 remove() 和 removeNode() 两个方法;算法思想如下:

  1. 首先判断当前节点是否包含待删除的数据,如果包含,则删除该节点;如果不包含,则比较当前节点上的数据和待删除树的的大小关系。如果待删除的数据小于当前节点的数据,则移至当前节点的左子节点继续比较;如果大于,则移至当前节点的右子节点继续比较。
  2. 如果待删除节点是叶子节点(没有子节点),那么只需要将从父节点指向它的链接指向变为null;
  3. 如果待删除节点含有一个子节点,那么原本指向它的节点需要做调整,使其指向它的子节点;
  4. 最后,如果待删除节点包含两个子节点,可以选择查找待删除节点左子树上的最大值或者查找其右子树上的最小值,这里我们选择后一种。

因此,我们需要一个查找树上最小值的方法,后面会用它找到最小值创建一个临时节点,将临时节点上的值复制到待删除节点,然后再删除临时节点;

我们上面说会用到两个方法,其中 remove 方法只是简单的接收待删除数据,调用 removeNode 删除它,主要工作在 removeNode 中完成,定义如下:

//删除操作

function remove( data ) {
  removeNode( this.root , data);
}

//查找最小值

function getSmallest(node) {
  if (node.left == null) {
    return node;
  }
  else {
    return getSmallest(node.left);
  }
}

//删除节点
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;
    }
    // 有2个子节点的节点
    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;
  }
}

现在我们来删除节点试试。

//删除根节点
nums.remove(23);

inOrder(nums.root);

// 3 16 22 37 45 99

成功了!现在,我们的BST算是完整了。

我们现在已经可以利用js是实现一个简单的BST了,实际中树的使用会很广泛,希望大家能多去了解了解树的数据结构,多动手实践,相信你会有不少的收获!

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码运行效果。

更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《JavaScript数学运算用法总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript数组操作技巧总结》、《JavaScript排序算法总结》、《JavaScript遍历算法与技巧总结》、《JavaScript查找算法技巧总结》及《JavaScript错误与调试技巧总结》

希望本文所述对大家JavaScript程序设计有所帮助。

(0)

相关推荐

  • JavaScript实现二叉树的先序、中序及后序遍历方法详解

    本文实例讲述了JavaScript实现二叉树的先序.中序及后序遍历方法.分享给大家供大家参考,具体如下: 之前学数据结构的时候,学了二叉树的先序.中序.后序遍历的方法,并用C语言实现了,下文是用js实现二叉树的3种遍历,并以动画的形式展现出遍历的过程. 整个遍历过程还是采用递归的思想,原理很粗暴也很简单 先序遍历的函数: function preOrder(node){ if(!(node==null)){ divList.push(node); preOrder(node.firstEleme

  • JS实现的二叉树算法完整实例

    本文实例讲述了JS实现的二叉树算法.分享给大家供大家参考,具体如下: <!DOCTYPE HTML> <head> <title>20130328BinaryTree</title> <metahttp-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <html> <body> <

  • JavaScript数据结构之二叉树的删除算法示例

    本文实例讲述了JavaScript数据结构之二叉树的删除算法.分享给大家供大家参考,具体如下: 从二叉查找树上删除节点的操作复杂程度取决于删除哪个节点.如果删除没有子节点的节点就非常简单,如果节点只有一个子节点,不管是左子节点还是右子节点,就变得稍微有点复杂,如果节点包含两个子节点就最复杂. 如果待删除节点是叶子节点,那么只需要将从父节点指向它的链接指向null. 如果待删除节点只包含一个子节点,那么原本指向它的节点就得使其指向它的子节点. 如果待删除节点包含两个子节点,那么我们可以采用两种方式

  • JavaScript数据结构之二叉查找树的定义与表示方法

    本文实例讲述了JavaScript数据结构之二叉查找树的定义与表示方法.分享给大家供大家参考,具体如下: 树是一种非线性的数据结构,以分层的方式存储数据.树被用来存储具有层级关系的数据,比如文件系统中的文件:树还被用来存储有序列表.这里将研究一种特殊的树:二叉树.选择树而不是那些基本的数据结构,是因为在二叉树上进行查找非常快(而在链表上查找则不是这样),为二叉树添加或删除元素也非常快(而对数组执行添加或删除操作则不是这样). 树是n个结点的有限集.最上面的为根,下面为根的子树.树的节点包含一个数

  • JavaScript数据结构之二叉树的遍历算法示例

    本文实例讲述了JavaScript数据结构之二叉树的遍历算法.分享给大家供大家参考,具体如下: 三种遍历的代码: function inOrder(node){//中序遍历 if(node!=null){ inOrder(node.left); document.write(node.show()+" "); inOrder(node.right); } } function preOrder(node){//先序遍历 if(node!=null){ document.write(no

  • Javascript实现从小到大的数组转换成二叉搜索树

    废话不多说了,直接给大家贴代码了,具体代码如下所示: var Array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var Tree = createTree(Array); console.log(Tree); // 构造一个节点 function Node(nodeData, leftData, rightData) { this.nodeData = nodeData; this.leftData = leftData; this.rightData = rig

  • JS实现二叉查找树的建立以及一些遍历方法实现

    二叉查找树是由节点和边组成的. 我们可以定义一个节点类Node,里面存放节点的数据,及左右子节点,再定义一个用来显示数据的方法: //以下定义一个节点类 function Node(data,left,right){ // 节点的键值 this.data = data; // 左节点 this.left = left; // 右节点 this.right = left; // 显示该节点的键值 this.show = show; } // 实现show方法 function show(){ re

  • JavaScript数据结构与算法之二叉树实现查找最小值、最大值、给定值算法示例

    本文实例讲述了JavaScript数据结构与算法之二叉树实现查找最小值.最大值.给定值算法.分享给大家供大家参考,具体如下: function Node(data,left,right) { this.data = data; this.left = left; this.right = right; this.show = show; } function show() { return this.data; } function BST() { this.root = null; this.

  • JavaScript实现的DOM树遍历方法详解【二叉DOM树、多叉DOM树】

    本文实例讲述了JavaScript实现的DOM树遍历方法.分享给大家供大家参考,具体如下: 二叉 DOM 树的遍历 function Tree() { var Node = function(key){ this.key = key; this.left = null; this.right = null; } root =null; } 前序遍历 首先访问根结点,然后遍历左子树,最后遍历右子树 Tree.prototype.preOrderTraverse = function(callbac

  • JavaScript数据结构之二叉树的查找算法示例

    本文实例讲述了JavaScript数据结构之二叉树的查找算法.分享给大家供大家参考,具体如下: 前面文章介绍了二叉树的遍历,现在谈谈在二叉树中进行查找.对二叉查找树来说,一般有以下三类查找:最大值,最小值和给定值. 查找最小值就是遍历左子树,直到找到最后一个结点,这是因为在二叉查找树中较小的值总是在左子节点上的. 代码如下: function getMin(){//查找最小值 var current=this.root;//指向根节点 while(current.left!=null){ cur

  • javascript实现二叉树遍历的代码

    前言: 紧接着上篇 二叉树的javascript实现 ,来说一下二叉树的遍历. 本次一本正经的胡说八道,以以下这个二叉树为例子进行遍历: 接着是要引入二叉树实现的代码: function Node(data, left, right) { this.data = data; this.left = left; this.right = right; this.show = show; } function show() { return this.data; } function BST() {

  • JavaScript实现二叉树定义、遍历及查找的方法详解

    本文实例讲述了JavaScript实现二叉树定义.遍历及查找的方法.分享给大家供大家参考,具体如下: 二叉树(binary tree) 在写这篇文章之前说一下数据结构和算法这个系列,这个系列包含了很多东西,比如啥子排序,线性表,广义表,树,图这些大家都是知道的,但是这些东西我们学了之后工作中能用到的又有多少呢,据我所知绝大部分公司,一线码农,屌丝,程序猿是用不到这些东西,既然这样为啥子我还要强调这个系列呢,本人觉得算法和数据结构是程序的基本功,前提想脱离一线码农,普通程序猿行列,说得通俗一点就是

随机推荐