广告
返回顶部
首页 > 资讯 > 精选 >JavaScript二叉搜索树构建操作实例分析
  • 894
分享到

JavaScript二叉搜索树构建操作实例分析

2023-07-02 18:07:01 894人浏览 泡泡鱼
摘要

本篇内容介绍了“javascript二叉搜索树构建操作实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!什么是二叉搜索树二叉搜索树首先它

本篇内容介绍了“javascript二叉搜索树构建操作实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

什么是二叉搜索树

二叉搜索树首先它是一棵二叉树,而且还满足下面这些特质:

  • 对于任何一个非空节点来说,它左子树上的值必须小于当前值

  • 对于任何一个非空节点来说,它右子树上的值必须大于当前值

  • 任何一颗子树满足上面的条件;

如下图所示:

JavaScript二叉搜索树构建操作实例分析

上图就是一颗二叉搜索树,我们就拿根节点来说,根节点的值71,它的左子树的值分别是22、35、46、53和66,这几个都是满足左子树小于当前值;它的右子树的值分别是78、87和98,这几个值是满足右子树大于当前值的;以此类推,所以上图就是一棵二叉搜索树。

根据二叉搜索树的特质,我们还能得到以下结论:

  • 二叉搜索树的任何一个节点的左子树、右子树都是一颗二叉搜索树;

  • 二叉搜索树的最小的节点是整颗树的最左下角的叶子节点

  • 二叉搜索树的最大的节点是整棵树的最右下角的叶子节点

构建一颗二叉搜索树

我们现在使用JavaScript来构建一颗二叉搜索树,要知道一颗二叉搜索树也是由一个一个节点组成,这里我们通过class创建一个节点类,

示例代码如下:

class BinarySearchTree {  constructor() {    // 初始化根节点    this.root = null  }  // 创建一个节点  node(val) {    return {      left: null, // 左子树      right: null, // 右子树      parent: null, // 父节点      val,    }  }}

这里一个节点由四部分组成,分别是指向左子树的指针、指向右子树的指针、指向父节点的指针以及当前值。

二叉搜索树的操作

关于二叉树的遍历操作我们在上一篇文章中已经介绍了,这里不在重复,这里主要介绍如下操作:

  • 插入操作

  • 查找操作

  • 删除操作

向二叉搜索树中插入数据

向一个二叉搜索树插入数据实现思路如下:

  • 判断root是否为空,如果为空则创建root;

  • 如果root非空,则需要判断插入节点的val比根节点的val是大还是小;

  • 如果比根节点小,说明是左子树的节点;

  • 如果比根节点大,说明是右子树的节点;

  • 上面两步重复执行,直到找到一个点,如果这个点小于我们要插入的值,且不存在右子树,将这个点作为其右叶子节点;如果这个点大于我们要插入的值,且不存在右子树,将这个点作为其左叶子节点。

示例代码如下:

// 创建要给插入节点的方法insertNode(val) {  const that = this  // 允许接受一个数组,批量插入  if (Object.prototype.toString.call(val) === '[object Array]') {    val.forEach(function (v) {      that.insertNode(v)    })    return  }  if (typeof val !== 'number') throw Error('插入的值不是一个数字')  const newNode = this.Node(val)  if (this.root) {    // 根节点非空    this.#insertNode(this.root, newNode)  } else {    // 根节点是空的,直接创建    this.root = newNode  }}// 私有方法,插入节点#insertNode(root, newNode) {  if (newNode.val < root.val) {    // 新节点比根节点小,左子树    if (root.left === null) {      // 如果左子树上没有内容,则直接插入,如果有,寻找下一个插入位置      root.left = newNode      root.left.parent = root    } else {      this.#insertNode(root.left, newNode)    }  } else {    // 新节点比根节点大,右子树    if (root.right === null) {      // 如果右子树上没有内容,则直接插入,如果有,寻找下一个插入位置      root.right = newNode      root.right.parent = root    } else {      this.#insertNode(root.right, newNode)    }  }}

在类中定义了insertNode方法,这个方法接受数值或者数值类型的数组,将其插入这个二叉搜索树中;插入方法我们定义了一个私有的#insertNode方法,用于节点的插入。

为了看到效果,我们这里定义了一个静态方法,用于中序遍历(因为中序遍历的顺序是左根右,在二叉搜索树中使用中序排序,最终结果是从小到大依次排序的)这个树,并返回一个数组,

示例代码如下:

// 中序遍历这个树static inorder(root) {  if (!root) return  const result = []  const stack = []  // 定义一个指针  let p = root  // 如果栈中有数据或者p不是null,则继续遍历  while (stack.length || p) {    // 如果p存在则一致将p入栈并移动指针    while (p) {      // 将 p 入栈,并以移动指针      stack.push(p)      p = p.left    }    const node = stack.pop()    result.push(node.val)    p = node.right  }  return result}

测试代码如下:

const tree = new BinarySearchTree()
tree.insertNode([71, 35, 87, 22, 53, 46, 66, 78, 98])
const arr = BinarySearchTree.inorder(tree.root)
console.log(arr) // [ 22, 35, 46, 53, 66,71, 78, 87, 98 ]

最终的树结构如下:

JavaScript二叉搜索树构建操作实例分析

查找二叉搜索树中的数据

现在我们封装一个find方法,用于查找二叉搜索树中的某个数据,假如我们查找66这个数据,利用上面那个树,

其查找思路如下图所示:

JavaScript二叉搜索树构建操作实例分析

递归方式实现如下:

find(val) {  if (typeof val !== 'number') throw Error('插入的值不是一个数字')  function r(node, val) {    // console.log(node)    if (!node) return    if (node.val < val) {      return r(node.right, val)    } else if (node.val > val) {      return r(node.left, val)    } else {      return node    }  }  return r(this.root, val)}

迭代方式实现如下:

find(val) {  if (typeof val !== 'number') throw Error('插入的值不是一个数字')  let node = this.root  while (node) {    if (node.val < val) {      // 进入右子树      node = node.right    } else if (node.val > val) {      // 进入左子树      node = node.left    } else {      return node    }  }  return}

两者相对来说,使用迭代的方式更优一些。

删除二叉搜索树的某个节点

前驱后继节点

在开始删除二叉搜索树中的某个节点之前,我们先来了解一下什么是前驱和后继节点;

  • 前驱节点指的是使用中序遍历当前二叉搜索树时,当前节点的上一个节点就是前驱节点,换一种说法就是在二叉搜索树中,当前节点的左子树的最大值,就是该节点的前驱节点

  • 后继节点指的是使用中序遍历当前二叉搜索树时,当前节点的下一个节点就是后继节点,换一种说法就是在二叉搜索树中,当前节点的右子树的最小值,就是该节点的后继节点

如下图所示:

JavaScript二叉搜索树构建操作实例分析

了解了什么是前驱和后继节点之后,现在我们来开始删除某个节点。

删除一个节点的三种情况

当删除的节点是叶子节点时,只需要将指向它的指针修改为null,即可,如下图所示:

JavaScript二叉搜索树构建操作实例分析

当需要删除的节点存在一个子节点时 需要将要删除节点的子节点的parent指针指向要删除节点的父节点,然后将当前要删除节点的父节点指向子节点即可,

如下图所示:

JavaScript二叉搜索树构建操作实例分析

当需要删除的节点存在一个子节点时, 删除步骤如下:

  • 找到当前节点的前驱或者后继节点,这里选择后继;

  • 然后将后继节点的值赋值给当前节点;

  • 删除后继节点。

如下图所示:

JavaScript二叉搜索树构建操作实例分析

现在我们将这些情况已经分析完成了,现在通过代码实现一下。

实现代码

实现代码如下:

remove(val) {  // 1. 删除节点  const cur = this.find(val)  if (!val) return false // 未找到需要删除的节点  if (!cur.left && !cur.right) {    // 1. 当前节点是叶子节点的情况    this.#removeLeaf(cur)  } else if (cur.left && cur.right) {    // 2. 当前节点存在两个子节点    // 2.1 找到当前节点的后继节点    const successorNode = this.#minNode(cur.right)    // 2.2 将后继节点的值赋值给当前值    cur.val = successorNode.val    if (!successorNode.left && !successorNode.right) {      // 2.3 后继节点是叶子节点,直接删除      this.#removeLeaf(successorNode)    } else {      // 2.4 后继节点不是叶子节点      // 2.4.1记录该节点的子节点,      let child =        successorNode.left !== null ? successorNode.left : successorNode.right      // 2.4.2 记录该节点的父节点      let parent = successorNode.parent      // 2.4.3 如果当前父节点的left是后继结点,则把后继结点的父节点的left指向后继结点的子节点      if (parent.left === successorNode) {        parent.left = child      } else {        // 2.4.4 如果不是,则让父节点的right指向后继结点的子节点        parent.right = child      }      // 2.4.5 修改子节点的parent指针      child.parent = parent    }    // 2.3 删除后继节点  } else {    // 记录当前节点的是否是父节点的左子树    const isLeft = cur.val < cur.parent.val    // 3. 仅存在一个子节点    if (cur.left) {      // 3.1 当前节点存在左子树      cur.parent[isLeft ? 'left' : 'right'] = cur.left      cur.left.parent = cur.parent    } else if (cur.right) {      // 3.2 当前节点存在右子树      cur.parent[isLeft ? 'left' : 'right'] = cur.right      cur.right.parent = cur.parent    }  }}// 删除叶子节点#removeLeaf(node) {  if (!node) return  const parent = node.parent  if (node.val < parent.val) {    // 当前要删除的叶子节点是左节点    parent.left = null  } else {    // 当前要删除的叶子节点是右节点    parent.right = null  }}// 查找最小值#minNode(node) {  if (!node) return  if (!node.left) return node  let p = node.left  while (p.left) {    p = p.left  }  return p}

完整代码

本篇文章中的完整代码如下:

class BinarySearchTree {  constructor() {    // 初始化根节点    this.root = null  }  // 创建一个节点  Node(val) {    return {      left: null, // 左子树      right: null, // 右子树      parent: null, // 父节点      val,    }  }    insertNode(val) {    const that = this    // 允许接受一个数组,批量插入    if (Object.prototype.toString.call(val) === '[object Array]') {      val.forEach(function (v) {        that.insertNode(v)      })      return    }    if (typeof val !== 'number') throw Error('插入的值不是一个数字')    const newNode = this.Node(val)    if (this.root) {      // 根节点非空      this.#insertNode(this.root, newNode)    } else {      // 根节点是空的,直接创建      this.root = newNode    }  }    #insertNode(root, newNode) {    if (newNode.val < root.val) {      // 新节点比根节点小,左子树      if (root.left === null) {        // 如果左子树上没有内容,则直接插入,如果有,寻找下一个插入位置        root.left = newNode        root.left.parent = root      } else {        this.#insertNode(root.left, newNode)      }    } else {      // 新节点比根节点大,右子树      if (root.right === null) {        root.right = newNode        root.right.parent = root      } else {        this.#insertNode(root.right, newNode)      }    }  }    find(val) {    if (typeof val !== 'number') throw Error('插入的值不是一个数字')    let node = this.root    while (node) {      if (node.val < val) {        // 进入右子树        node = node.right      } else if (node.val > val) {        // 进入左子树        node = node.left      } else {        return node      }    }    return  }  //   // find(val) {  //   if (typeof val !== 'number') throw Error('插入的值不是一个数字')  //   function r(node, val) {  //     // console.log(node)  //     if (!node) return  //     if (node.val < val) {  //       return r(node.right, val)  //     } else if (node.val > val) {  //       return r(node.left, val)  //     } else {  //       return node  //     }  //   }  //   return r(this.root, val)  // }  remove(val) {    // 1. 删除节点    const cur = this.find(val)    if (!val) return false // 未找到需要删除的节点    if (!cur.left && !cur.right) {      // 1. 当前节点是叶子节点的情况      this.#removeLeaf(cur)    } else if (cur.left && cur.right) {      // 2. 当前节点存在两个子节点      // 2.1 找到当前节点的后继节点      const successorNode = this.#minNode(cur.right)      // 2.2 将后继节点的值赋值给当前值      cur.val = successorNode.val      if (!successorNode.left && !successorNode.right) {        // 2.3 后继节点是叶子节点,直接删除        this.#removeLeaf(successorNode)      } else {        // 2.4 后继节点不是叶子节点        // 2.4.1记录该节点的子节点,        let child =          successorNode.left !== null ? successorNode.left : successorNode.right        // 2.4.2 记录该节点的父节点        let parent = successorNode.parent        // 2.4.3 如果当前父节点的left是后继结点,则把后继结点的父节点的left指向后继结点的子节点        if (parent.left === successorNode) {          parent.left = child        } else {          // 2.4.4 如果不是,则让父节点的right指向后继结点的子节点          parent.right = child        }        // 2.4.5 修改子节点的parent指针        child.parent = parent      }      // 2.3 删除后继节点    } else {      // 记录当前节点的是否是父节点的左子树      const isLeft = cur.val < cur.parent.val      // 3. 仅存在一个子节点      if (cur.left) {        // 3.1 当前节点存在左子树        cur.parent[isLeft ? 'left' : 'right'] = cur.left        cur.left.parent = cur.parent      } else if (cur.right) {        // 3.2 当前节点存在右子树        cur.parent[isLeft ? 'left' : 'right'] = cur.right        cur.right.parent = cur.parent      }    }  }  // 删除叶子节点  #removeLeaf(node) {    if (!node) return    const parent = node.parent    if (node.val < parent.val) {      // 当前要删除的叶子节点是左节点      parent.left = null    } else {      // 当前要删除的叶子节点是右节点      parent.right = null    }  }  // 查找最小值  #minNode(node) {    if (!node) return    if (!node.left) return node    let p = node.left    while (p.left) {      p = p.left    }    return p  }  // 中序遍历这个树  static inorder(root) {    if (!root) return    const result = []    const stack = []    // 定义一个指针    let p = root    // 如果栈中有数据或者p不是null,则继续遍历    while (stack.length || p) {      // 如果p存在则一致将p入栈并移动指针      while (p) {        // 将 p 入栈,并以移动指针        stack.push(p)        p = p.left      }      const node = stack.pop()      result.push(node.val)      p = node.right    }    return result  }}const tree = new BinarySearchTree()tree.insertNode([71, 35, 84, 22, 53, 46, 66, 81, 83, 82, 88, 98])console.log(BinarySearchTree.inorder(tree.root)) // [ 22, 35, 46, 53, 66, 71, 81, 82, 83, 84, 88, 98 ]tree.remove(71console.log(BinarySearchTree.inorder(tree.root)) // [ 22, 35, 46, 53, 66, 81, 82, 83, 84, 88, 98 ]

“JavaScript二叉搜索树构建操作实例分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: JavaScript二叉搜索树构建操作实例分析

本文链接: https://www.lsjlt.com/news/343499.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • JavaScript二叉搜索树构建操作实例分析
    本篇内容介绍了“JavaScript二叉搜索树构建操作实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!什么是二叉搜索树二叉搜索树首先它...
    99+
    2023-07-02
  • JavaScript二叉搜索树构建操作详解
    目录前言什么是二叉搜索树构建一颗二叉搜索树二叉搜索树的操作向二叉搜索树中插入数据查找二叉搜索树中的数据删除二叉搜索树的某个节点前驱后继节点删除一个节点的三种情况实现代码完整代码总结前...
    99+
    2022-11-13
  • C++二叉搜索树实例分析
    本篇内容介绍了“C++二叉搜索树实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!独一无二的二叉搜索树Given an integer&...
    99+
    2023-06-19
  • Java数据结构之二叉搜索树实例分析
    这篇文章主要介绍“Java数据结构之二叉搜索树实例分析”,在日常操作中,相信很多人在Java数据结构之二叉搜索树实例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java数据结构之二叉搜索树实例分析”的疑...
    99+
    2023-06-30
  • java二叉搜索树使用实例分析
    本篇内容主要讲解“java二叉搜索树使用实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java二叉搜索树使用实例分析”吧!概念二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性...
    99+
    2023-06-29
  • Java创建二叉搜索树,实现搜索,插入,删除的操作实例
    Java实现的二叉搜索树,并实现对该树的搜索,插入,删除操作(合并删除,复制删除)首先我们要有一个编码的思路,大致如下: 1、查找:根据二叉搜索树的数据特点,我们可以根据节点的值得比较来实现查找,查找值大于当前节点时向右走,反之向左走!2、...
    99+
    2023-05-30
    java 二叉搜索树 搜索
  • Java字符串,数组及二叉搜索树实例分析
    本文小编为大家详细介绍“Java字符串,数组及二叉搜索树实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java字符串,数组及二叉搜索树实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。题目一&nbs...
    99+
    2023-06-29
  • C++使用LeetCode实现二叉搜索树的示例分析
    这篇文章将为大家详细讲解有关C++使用LeetCode实现二叉搜索树的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Given an integer n, generate all st...
    99+
    2023-06-20
  • Java数据结构超详细分析二叉搜索树
    目录1.搜索树的概念2.二叉搜索树的简单实现2.1查找2.2插入2.3删除2.4修改3.二叉搜索树的性能 1.搜索树的概念 二叉搜索树是一种特殊的二叉树,又称二叉查找树,二叉排序树,...
    99+
    2022-11-13
  • Java二叉搜索树增、插、删、创的示例分析
    小编给大家分享一下Java二叉搜索树增、插、删、创的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!①概念二叉搜索树又称二叉排序树,它或者是一棵空树**,或者是具有以下性质的二叉树:若它的左子树不为空,则左子树上所有...
    99+
    2023-06-29
  • C++数据结构二叉搜索树的实现应用与分析
    目录概念二叉搜索树的实现基本框架二叉搜索树的插入二叉搜索树的查找二叉搜索树的删除(重点)二叉搜索树的应用二叉树性能分析总结⭐️博客代码已上传至gitee:https://gitee....
    99+
    2022-11-13
  • C++简单实现与分析二叉搜索树流程
    目录二叉搜索树二叉搜索树的重要操作二叉搜索树实现(key模型)二叉搜索树的应用二叉搜索树的实现(key/value模型)二叉搜索树 二叉搜索树又被称为二叉排序树。它可以是一个空树,如...
    99+
    2022-11-13
  • 纯C++二叉树相关操作实例代码分析
    这篇“纯C++二叉树相关操作实例代码分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“纯C++二叉树相关操作实例代码分析”文...
    99+
    2023-07-02
  • JavaScript二叉树及遍历算法实例分析
    这篇“JavaScript二叉树及遍历算法实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看...
    99+
    2022-10-19
  • Java实题演练二叉搜索树与双向链表分析
    目录二叉搜索树与双向链表知识点-二叉树递归知识点-二叉搜索树思路代码二叉搜索树与双向链表 OJ链接 二叉树搜索树与双向链表 描述 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双...
    99+
    2022-12-08
    Java二叉搜索树 Java双向链表
  • JavaScript数据结构与算法之二叉树插入节点、生成二叉树的示例分析
    小编给大家分享一下JavaScript数据结构与算法之二叉树插入节点、生成二叉树的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解...
    99+
    2022-10-19
  • javascript数据结构之多叉树经典操作的示例分析
    这篇文章给大家分享的是有关javascript数据结构之多叉树经典操作的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。多叉树可以实现复杂的数据结构的存储,通过遍历方法可以...
    99+
    2022-10-19
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作