数据结构TypeScript之二叉查找树实现详解

数据结构TypeScript之二叉查找树实现详解

什么是二叉查找树

二叉查找树(Binary Search Tree,简称BST)是一种基础的数据结构,也是一种常用的搜索算法。它通过以二叉树的形式表示各个结点之间的关系,实现了快速查找、添加、删除等操作。对于任何一个节点,其左子树上的节点值均小于该节点的值,右子树上的节点值均大于该节点的值。

二叉查找树的实现

下面我们将分别介绍二叉查找树的构造方法、插入、查找和删除等操作的实现。

构造方法

构造方法的主要作用是创建一棵空树。由于我们要实现的是泛型的BST,因此需要在定义中使用泛型T。在新建二叉查找树时,我们仅需设置其根节点为null即可。

class BinarySearchTree<T> {
  private root: BSTNode<T> | null;

  constructor() {
    this.root = null;
  }
}

插入

插入操作分为两步:查找插入节点的位置和将新节点插入到该位置。

  1. 查找插入节点的位置

查找插入节点的位置需要从根节点开始递归查找。对于当前节点,如果插入节点的值小于该节点的值,则继续递归查找该节点的左子树;否则继续递归查找该节点的右子树。如果递归到最底层节点时,仍未找到合适的插入位置,则说明该节点为插入节点的位置。

private findInsertPos(val: T): BSTNode<T> {
  let current = this.root;
  let parent = null;

  while (current !== null) {
    parent = current;
    if (val < current.val) {
      current = current.left;
    } else if (val > current.val) {
      current = current.right;
    } else {
      // 已存在该节点,不需要插入
      return null;
    }
  }

  // parent为插入节点的父节点
  return parent;
}
  1. 将新节点插入到该位置

找到插入位置的父节点后,我们需要将新节点插入到该位置。由于二叉查找树要求,所有左子树上的节点值均小于该节点的值,右子树上的节点值均大于该节点的值,因此需要先将新节点的值与父节点进行比较,然后决定将新节点插入到左子树或右子树。

public insert(val: T): void {
  const parent = this.findInsertPos(val);

  if (parent === null) {
    // 已存在该节点,不需要插入
    return;
  }

  const node = new BSTNode(val);
  if (parent.val > val) {
    parent.left = node;
  } else {
    parent.right = node;
  }
}

查找

查找操作的实现与插入操作相似,也需要从根节点开始递归查找,但对于查找的值与当前节点的值大小关系,我们有三种情况需要考虑:查找值等于当前节点值、查找值小于当前节点值、查找值大于当前节点值。

private find(val: T, current: BSTNode<T>): BSTNode<T> {
  if (current === null) {
    return null;
  }

  if (val === current.val) {
    // 查找值等于当前节点值
    return current;
  } else if (val < current.val) {
    // 查找值小于当前节点值,递归查找左子树
    return this.find(val, current.left);
  } else {
    // 查找值大于当前节点值,递归查找右子树
    return this.find(val, current.right);
  }
}

public search(val: T): boolean {
  return this.find(val, this.root) !== null;
}

删除

删除操作最复杂的地方在于删除节点时需要查找该节点的后继(即以当前节点为根的子树上的最小节点)或前驱(即以当前节点为根的子树上的最大节点)。

  1. 查找目标节点

查找目标节点时需要从根节点开始递归查找,找到要删除的节点。

private findDeleteNode(val: T, current: BSTNode<T>, parent: BSTNode<T>): BSTNode<T> {
  if (current === null) {
    return null;
  } else if (val === current.val) {
    // 找到目标节点
    return { node: current, parent };
  } else if (val < current.val) {
    // 查找左子树
    return this.findDeleteNode(val, current.left, current);
  } else {
    // 查找右子树
    return this.findDeleteNode(val, current.right, current);
  }
}
  1. 删除目标节点

当找到目标节点后,我们需要考虑以下三种情况。

  • 目标节点没有子节点:直接删除目标节点即可;
  • 目标节点只有一个子节点:将目标节点的父节点指向目标节点的子节点;
  • 目标节点有两个子节点:找到目标节点的后继或前驱,并将其值赋给目标节点,然后删除后继或前驱节点。
public delete(val: T): void {
  let { node, parent } = this.findDeleteNode(val, this.root, null);

  if (node === null) {
    // 没有找到目标节点
    return;
  }

  if (node.left === null && node.right === null) {
    // 目标节点没有子节点
    if (parent === null) {
      // 删除根节点
      this.root = null;
    } else if (parent.left === node) {
      parent.left = null;
    } else {
      parent.right = null;
    }
  } else if (node.left === null || node.right === null) {
    // 目标节点只有一个子节点
    const child = node.left || node.right;
    if (parent === null) {
      // 删除根节点
      this.root = child;
    } else if (parent.left === node) {
      parent.left = child;
    } else {
      parent.right = child;
    }
  } else {
    // 目标节点有两个子节点,找到后继节点
    let successor = node.right;
    let successorParent = node;
    while (successor.left !== null) {
      successorParent = successor;
      successor = successor.left;
    }

    // 将后继节点的值赋给目标节点,然后删除后继节点
    node.val = successor.val;
    if (successorParent.left === successor) {
      successorParent.left = successor.right;
    } else {
      successorParent.right = successor.right;
    }
  }
}

示例

假设我们有一组整数需要插入到二叉查找树中。

const nums = [20, 5, 12, 30, 28, 15, 18, 35, 25, 40];
const bst = new BinarySearchTree<number>();

for (const num of nums) {
  bst.insert(num);
}

插入完成后,我们可以使用以下方法检查树是否构造正确,并输出中序遍历结果。

function inOrder(node: BSTNode<any>, arr: any[]): void {
  if (node !== null) {
    inOrder(node.left, arr);
    arr.push(node.val);
    inOrder(node.right, arr);
  }
}

const res: any[] = [];
inOrder(bst.root, res);
console.log(res);

输出结果为:

[5, 12, 15, 18, 20, 25, 28, 30, 35, 40]

接下来,我们可以使用以下代码测试删除操作。

bst.delete(20);  // 删除根节点
bst.delete(5);   // 删除叶子节点
bst.delete(30);  // 删除有一子节点的节点
bst.delete(15);  // 删除有两子节点的节点

const res: any[] = [];
inOrder(bst.root, res);
console.log(res);

输出结果为:

[12, 18, 25, 28, 35, 40]

从结果中可以看出,二叉查找树插入和删除操作都能够正常工作,可以使用该数据结构实现高效的查找、添加、删除等操作。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:数据结构TypeScript之二叉查找树实现详解 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • C语言全面讲解顺序表使用操作

    C语言全面讲解顺序表使用操作 什么是顺序表 顺序表(Sequential List)是一种常见的数据结构,它由一组连续的存储单元组成,并且支持随机访问。通常我们使用数组来实现顺序表。 顺序表的基本操作 初始化 在使用顺序表之前,需要先进行初始化。顺序表的初始化包括两个步骤:指定顺序表的大小,申请内存空间。具体代码如下: #define MAXSIZE 100…

    数据结构 2023年5月17日
    00
  • c语言实现单链表算法示例分享

    下面是详细的攻略。 C语言实现单链表算法示例分享 什么是单链表 单链表是一种数据结构,它由一个个节点组成,每个节点包含两个部分:一个是数据部分,另一个是指针部分,指针部分指向下一个节点的位置。单链表的节点是动态分配的,可以随时插入、删除,是一种非常灵活的数据结构。 为什么要使用单链表 在进行一些操作时,数组或者普通的指针会遇到很多麻烦。比如在删除数组元素时,…

    数据结构 2023年5月17日
    00
  • C语言 数据结构中栈的实现代码

    下面是关于C语言中栈的实现代码的详细攻略: 栈的概念 栈是一种只能在一端进行插入或删除操作的线性数据结构,它具有后进先出(Last In First Out, LIFO)的特点。通俗的说,就像大家在平时搭积木那样,搭积木的时候总是从最下面开始往上搭,拿积木的时候总是从最上面的积木开始拿起,栈就是这么一个先进后出的数据结构。 栈的实现方法 栈的实现方法比较多,…

    数据结构 2023年5月17日
    00
  • 常用的Java数据结构知识点汇总

    常用的Java数据结构知识点汇总 简介 Java中的数据结构是Java程序开发中非常重要的一部分。掌握常用的数据结构知识点是编写高效、优秀的Java程序的关键之一。本文将详细讲解Java中常用的数据结构知识点,并提供代码示例说明。 数组(Array) 数组是一组相同类型的数据集合,通过数组下标来访问数据,数组长度确定后就无法改变。在Java中,数组可以是基本…

    数据结构 2023年5月17日
    00
  • C语言数据结构哈希表详解

    C语言数据结构哈希表详解 什么是哈希表? 哈希表(Hash Table)是一种采用散列函数(hash函数)将数据映射到一个固定长度的数组中,并且以 O(1) 的时间复杂度进行数据插入、查找、删除操作的数据结构。哈希表主要由以下三个组成部分构成:- 数组:用于存储映射到对应下标上的数据。- 散列函数:将数据映射到数组下标上的规则。- 冲突处理方式:当不同的数据…

    数据结构 2023年5月16日
    00
  • 用C语言举例讲解数据结构中的算法复杂度结与顺序表

    让我来为你讲解“用C语言举例讲解数据结构中的算法复杂度结与顺序表”的完整攻略。具体如下: 一、算法复杂度 1.1 什么是算法复杂度 算法复杂度是衡量算法运行效率的重要指标。包括时间复杂度和空间复杂度。时间复杂度指算法解决问题所用的时间,通常用大O符号表示;空间复杂度指算法解决问题所需的内存空间大小。 1.2 如何分析算法复杂度 可以从以下三个方面来分析算法复…

    数据结构 2023年5月17日
    00
  • C语言编程数据结构线性表之顺序表和链表原理分析

    C语言编程数据结构线性表之顺序表和链表原理分析 线性表的定义 线性表是由n(n>=0)个数据元素a1、a2、…、an组成的有限序列,通常用(a1, a2, …, an)表示,其中a1是线性表的第一个元素,an是线性表的最后一个元素。线性表中的元素个数n称为线性表的长度,当n=0时,线性表为空表。 线性表的分类 根据存储结构,线性表可以分为顺序表…

    数据结构 2023年5月17日
    00
  • el-tree的实现叶子节点单选的示例代码

    下面我将详细讲解“el-tree的实现叶子节点单选的示例代码”的完整攻略。 示例代码实现 el-tree 的实现叶子节点单选,需要在 el-tree 上绑定 @check-change 事件,并通过 check-strictly 属性来配置选择模式。代码示例如下: <template> <el-tree :data="data&q…

    数据结构 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部