数据结构—完全二叉堆

news/2024/5/19 5:10:40 标签: 数据结构, , 堆排序

1. 简介

  • 完全二叉可用于实现优先队列

  • 当然,使用数组或列表也可以实现优先队列,但通常需要先将其中的所有数据进行排序才可,即首先维护一种全序关系。

  • 但事实上,优先队列只要能够确定全局优先级最高的 entry 即可,而不要求全局先有序。

  • 完全二叉无需先对所有数据进行排序即可确定全局优先级最高的 entry,因此更加适用于优先队列的实现。

2. 定义

  • 结构性:完全二叉的逻辑结构就是一棵完全二叉树,但可以使用数组来实现。

  • 序性:对于最小来说,中每个父节点都要小于等于其子节点;对于最大来说,中每个父节点都要大于等于其子节点。

  • 设节点总数为 n n n,则的高度为 O ( log ⁡ n ) O(\log n) O(logn),插入操作、删除优先级最高的 entry 的操作的时间复杂度均为 O ( log ⁡ n ) O(\log n) O(logn)

  • 假设自顶向下对的每一层、从左到右对每个节点进行编号,且编号从 0 开始,则有:

    • 假设节点 x x x 的编号为 i i i,且存在左右孩子,则 x x x 的左孩子的编号为 2 i + 1 2i+1 2i+1,右孩子的编号为 2 i + 2 2i+2 2i+2
    • 假设节点 x x x 的编号为 i i i,且存在父节点,则其父节点的编号为 ⌊ ( i − 1 ) / 2 ⌋ \lfloor (i-1)/2 \rfloor (i1)/2

以下以最小为例。

在这里插入图片描述

3. 插入

假设待插入的 entry 为 e e e

(1)首先在尾之后的一个位置插入一个新节点,并存储 e e e
(2)假设当前节点为 x x x(初始时, x x x 为尾后节点),其父节点为 p p p
(3)如果 p . e n t r y ≤ x . e n t r y p.entry \le x.entry p.entryx.entry,则成功返回;否则,
(4)上滤:如果 x . e n t r y < p . e n t r y x.entry \lt p.entry x.entry<p.entry,则令 x . e n t r y x.entry x.entry p . e n t r y p.entry p.entry 互换位置,然后令 x x x 指向 p p p,令 p p p 指向 p p p 的父节点,接着回到步骤(3)。

4. 删除

待删除的 entry 总是位于顶。

(1)首先,交换顶和尾的 entry,然后将的大小减一(尾前移),以删除顶元素(被删除的元素位于尾之后的一个位置);
(2)令当前节点为 p p p(初始时, p p p顶);
(3)令 x x x p p p 的两个孩子节点中较小的一个;
(4)如果 p . e n t r y ≤ x . e n t r y p.entry \le x.entry p.entryx.entry,则成功返回;否则,
(5)下滤:如果 x . e n t r y < p . e n t r y x.entry \lt p.entry x.entry<p.entry,则令 x . e n t r y x.entry x.entry p . e n t r y p.entry p.entry 互换位置,然后令 p p p 指向 x x x,接着回到步骤(3)。

5. 建

如下图所示,假设 H 0 , H 1 H_0, H_1 H0,H1 是两个最小,现欲将节点 p p p 和最小 H 0 , H 1 H_0,H_1 H0,H1 合并为一个新的最小

所需操作是:

(1)令 H 0 H_0 H0 p p p 的左孩子, H 1 H_1 H1 p p p 的右孩子;
(2)接着,从 p p p 开始执行下滤操作即可。

在这里插入图片描述
因此,给定一个数组,其大小为 n n n,现欲使用它构建一个最小所需的操作为:

(1)设当前节点为 x x x,其节点编号为 m m m,初始时, x x x的最后一个内部节点,编号为 m ← ⌊ ( n − 2 ) / 2 ⌋ m \leftarrow \lfloor (n-2)/2 \rfloor m(n2)/2
(2)如果 m < 0 m \lt 0 m<0,则返回;否则,从 x x x 开始执行下滤操作(先将子树 x x x 转换为一个最小);
(3)接着将 x x x 指向前一个内部节点,其编号为 m ← m − 1 m \leftarrow m-1 mm1,然后回到步骤(2)。

上述建操作的时间复杂度为 O ( n ) O(n) O(n)

6. 排序

总体而言,排序的过程为:对执行 n n n 次删除操作,因此其时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)

因为每次删除操作都会将被删除的顶元素放到尾,而且的大小也在不断地减一(尾随之不断地向前移动),因此,最后的数组将包含排序后的元素。

对于最小来说,排序的直接结果是逆序的;而对于最大来说,则是顺序的。


http://www.niftyadmin.cn/n/1663155.html

相关文章

查找算法:并行搜索

并发的基本概念 所谓并发是在同一实体上的多个事件同时发生&#xff0c;并发编程是指在同一台计算机上“同时”出路多个任务。 要理解并发编程&#xff0c;我们必须要理解如下一些基本概念&#xff1a; 计算机就像一座工厂&#xff0c;时刻运行&#xff0c;为人类服务&#…

Linux系统编程—信号

1. 简介 信号有时也称为软件中断。 一个进程能够向另一个进程发送信号&#xff0c;因此信号也可作为一种同步技术。 传统或标准信号的编号范围是 1~31&#xff0c;其余信号为实时信号。 $ kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP6) SIGABRT 7) SI…

Jenkins 安装、配置和使用

导言 一直对持续集成和自动化部署感兴趣&#xff0c;现在有机会学习和使用Jenkins了&#xff0c;在这过程中&#xff0c;由于网上的资料参差不齐&#xff0c;所以走了不少弯路&#xff0c;所以我打算编写文档来记录自己在Jenkins之中走过的路。 1.Jenkins 安装方式 Windows和L…

链表 详解(单链表、循环链表、双向链表、Linux内核“共享”双链表)C/C++

文章结尾附上了单链表、循环链表、双向链表及Linux内核“共享”双链表完整代码 目录 一、链表原理精讲 二、单链表算法实现 2.1单链表的概念 2.2单链表初始化 2.3单链表增加元素 2.3.1前插法 2.3.2尾插法 2.3.3任意位置插入 2.4单链表遍历 2.5单链表获取元素 2.6单…

从零开始实现一个React(二):实现组件功能

前言 在上一篇文章JSX和虚拟DOM中&#xff0c;我们实现了基础的JSX渲染功能&#xff0c;但是React的意义在于组件化。在这篇文章中&#xff0c;我们就要实现React的组件功能。 React定义组件的方式可以分为两种&#xff1a;函数和类&#xff0c;我们姑且将两种不同方式定义的组…

算法—二分搜索法

可以使用二分搜索法在一个有序的数组中快速地定位指定的元素。二分搜索法可以不断地将搜索范围减半&#xff0c;从而将时间复杂度控制为 O(log⁡n)O(\log n)O(logn)。 需要注意的是&#xff0c;二分搜索法要求输入的数据是有序的。 问题1&#xff1a;找到任意一个目标 给定一…

Java中抽象类和接口有什么异同

###一、抽象类 抽象类 包含一个抽象方法的类就是抽象类抽象方法 声明而未被实现的方法&#xff0c;抽象方法必须使用abstract关键词字声明public abstract class People { //关键词abstract&#xff0c;声明该类为抽象类public int age;public void Num() {}public abstract N…

数据结构—散列表

1. 简介 散列表的插入、删除和搜索操作都具有 O(1)O(1)O(1) 的时间复杂度。 通常而言&#xff0c;散列表的底层就是一个大小固定的数组&#xff0c;且散列表的大小通常需要为素数。 为了插入 (key,value)(key,value)(key,value) 键值对&#xff0c;需要首先使用散列函数对键进…