数据结构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数据结构之双链表详细示例分析的完整攻略。 双链表简介与定义 双链表是链表的一种,在链表中每一个节点都有一个指针域,指向下一个节点,这个指针域称为next指针;而在双链表中每一个节点也有两个指针域,一个指向前驱节点,另一个指向后继节点,即prev指针与next指针。由于双链表存在两个指针域,因此它支持双向遍历,无论是正向还是…

    数据结构 2023年5月17日
    00
  • golang优先级队列的实现全过程

    下面是关于”golang优先级队列的实现全过程”的详细讲解。 什么是优先级队列? 优先级队列是一种常用的数据结构,它可以帮我们按照一定规则(即优先级)将元素按照大小排序,并支持快速插入、删除和查询最大或最小的元素等操作。我们可以将优先级队列想象成一个具有优先级的、自动排序的队列,其中优先级高的元素(比如数字大的元素)排在前面,优先级低的元素(比如数字小的元素…

    数据结构 2023年5月17日
    00
  • 【ACM算法竞赛日常训练】DAY3题解与分析【旅游】【tokitsukaze and Soldier】

    DAY3共2题: 旅游 tokitsukaze and Soldier ? 作者:Eriktse? 简介:19岁,211计算机在读,现役ACM银牌选手?力争以通俗易懂的方式讲解算法!❤️欢迎关注我,一起交流C++/Python算法。(优质好文持续更新中……)?? 原文链接(阅读原文获得更好阅读体验): 旅游 题目传送门:https://ac.nowcoder…

    算法与数据结构 2023年4月18日
    00
  • Python数据结构之翻转链表

    对于“Python数据结构之翻转链表”的完整攻略,我会按照以下顺序进行讲解: 1.什么是链表? 2.如何翻转链表? 3.示例1:翻转一个简单的链表 4.示例2:翻转一个带环的链表 5.如何在Python中实现翻转链表? 接下来,我会详细讲解每个部分。 什么是链表? 链表是一种数据结构,它由一系列的节点组成,每个节点包含了数据和指向下一个节点的指针。链表有很多…

    数据结构 2023年5月17日
    00
  • 详解python数据结构之栈stack

    详解Python数据结构之栈stack 什么是栈stack 栈是一种先进后出(LIFO)的数据结构,只允许在表的一端进行插入和删除操作。栈的入口称为栈底,出口称为栈顶。栈常用于表达式求值、函数调用等场景。 栈的操作 栈的基本操作包括入栈(push)和出栈(pop)。其他常用的操作有判断栈是否为空(isEmpty)、获取栈的大小(size)和获取栈顶元素(pe…

    数据结构 2023年5月17日
    00
  • C语言数据结构算法基础之循环队列示例

    标题:C语言数据结构算法基础之循环队列示例 1. 简介 循环队列是一种常见的数据结构,它采用固定大小的数组来模拟队列的数据结构,可以高效地处理队列的进出操作。本文将会讲解循环队列的实现原理和示例代码。 2. 循环队列基本原理 循环队列通过两个指针front和rear来实现队列的添加和删除操作。在初始化时,front和rear的初始值都为0。每当数据进入队列时…

    数据结构 2023年5月17日
    00
  • Java数据结构之线性表

    Java数据结构之线性表完整攻略 什么是线性表 线性表是n个数据元素的有限序列,其中数据元素的类型相同。线性表中含有首元素和末元素。若表中只有一个数据元素,则该数据元素既是首元素又是末元素,这个数据元素成为线性表的唯一元素。 线性表的基本操作 初始化操作 initList(List L):建立一个空的线性表L 插入操作 insert(List L, int …

    数据结构 2023年5月17日
    00
  • GPS北斗卫星时间同步系统助力电力自动化网络系统

    GPS北斗卫星时间同步系统助力电力自动化网络系统 GPS北斗卫星时间同步系统助力电力自动化网络系统 京准电子官微——ahjzsz 前言 近几年来,随着电力自动化水平的提高,在电力中计算机监控系统、微机保护装置、微机故障录波装置以及各类数据管理机得到了广泛的应用,而这些自动装置的配合工作需要有一个精确统一的时间。当电力系统发生故障时,既可实现全站各系统在统一时…

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