数据结构——顺序二叉树——堆

这篇具有很好参考价值的文章主要介绍了数据结构——顺序二叉树——堆。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.树的相关概念

        在介绍二叉树之前,我们首先要明确树是什么。

        树用我们的通常认识来判断应该是一种植物,从根向上生长,分出许多的树枝并长出叶子。对于数据结构中的树而言,其结构也正是从树的特征中剥离出来的。树结构是一种非线性数据结构,具有一个根结点,这一个根节点连接着其他若干个结点,这些结点也同样可以连接这其他若干个结点,如此形成的数据结构我们就称为树。

数据结构——顺序二叉树——堆,数据结构

        子树即树的子集,我们可以认为根节点延伸出的若干个节点实际上是若干棵子树。注意,在树形结构的定义下子树之间不允许交集的存在,即树中不允许存在环。

        一个结点或树具有一些性质,一个结点的指的是其所含有的子树的个数,而一个树的度指的是这棵树的结点中最大的度;一个结点的层次指的是其对于根节点而言所处的层数,而一个树的高度/深度指的是这棵树的结点中最大的层次。

        按照结点的度和层次特点,可以分为:根节点——第一层;叶子结点——度为0;分支结点——度不为0;根据我们生活中的伦理关系延伸,一个结点的前驱结点称为父结点,后继结点称为子节点,同一个父结点的结点称为兄弟节点

2.二叉树相关概念与存储结构

2.1 二叉树概念

        二叉树是一种特殊的树。满足度为2的树就是二叉树,所以对于任意一个二叉树的节点我们都可以将其视为是一个根节点连接着左右两个子树的结构。

        数据结构——顺序二叉树——堆,数据结构

        满二叉树是一种特殊的二叉树,其每一层结点的个数都是满的。因此对于一个n层的满二叉树,其结点个数为个。

        完全二叉树也是一种特殊的二叉树,其与和满二叉树相似,结点按层依次排序,到所有结点连接前不允许出现空位。

数据结构——顺序二叉树——堆,数据结构

         值得注意的是:二叉树的度为0的结点个数一定比度为2的结点个数多一个

2.2二叉树的存储结构

2.2.1 顺序结构

        顺序结构采取数组来存储二叉树。数组根据二叉树按层的顺序将每个结点的值存进数组的对应位置。对于一棵完全二叉树,其父子结点之间在下标上存在固定的递推关系式,对于一个根节点位于下标为0位置的树而言:左孩子下标=父结点下标*2+1右孩子下标=父结点下标*2+2父结点下标=孩子结点下标/2。根据这个规律,我们便可以在数组中存下所有的二叉树结点并可以找到结点相关联的其它结点。

        可见,顺序二叉树按位置以此存储,这就意味着如果不是完全二叉树,那么在数组中就会出现空位。因此顺序二叉树一般只会用于完全二叉树来避免空间浪费。

数据结构——顺序二叉树——堆,数据结构

2.2.2 链式结构

        链式二叉树使用链表来表示,链就作为二叉树的逻辑关系指示。链式二叉树结点存储着自己的数据和两个指针,指针分别指向左孩子和右孩子,由此组成最后的完整的二叉树。链式二叉树可以存储任意形式的二叉树,我们一般采用二叉链。

数据结构——顺序二叉树——堆,数据结构

2.堆

2.1 堆的概念

        对于一个集合k,将其所有元素按照顺序二叉树的方式存入一个一维数组,对任意元素,若满足数据结构——顺序二叉树——堆,数据结构数据结构——顺序二叉树——堆,数据结构则为小堆;若满足数据结构——顺序二叉树——堆,数据结构数据结构——顺序二叉树——堆,数据结构则为大堆。

        通俗解释来说:堆是一个完全二叉树,当所有的父结点都小于等于自己的子结点时,构成了小堆;当所有的父结点都大于等于自己的子结点时,构成了大堆。

数据结构——顺序二叉树——堆,数据结构

 3.2 堆的工程

3.2.1 堆的定义

        因为我们已经明确了,堆是使用数组进行存储,所以堆的实现方式应该和顺序表相同。

typedef int HPDataType;

typedef struct Heap
{
	HPDataType* data;
	int size;
	int capacity;
}HP;

 3.2.2 堆的函数接口

 3.2.2.1 堆的初始化与销毁

        堆的初始化与销毁和顺序表相同,不再过多叙述。

void HeapInit(HP* php)
{
	assert(php);

	php->data = NULL;
	php->capacity = php->size = 0;
}

void HeapDestroy(HP* php)
{
	assert(php);

	free(php->data);
	php->data = NULL;
	php->capacity = php->size = 0;
}
3.2.2.2 堆的大小、判空、取堆顶元素

        这些都是很基础的操作,与栈和队列相似,也不再详细说道。

int HeapSize(HP* php)
{
	assert(php);

	return php->size;
}

bool HeapEmpty(HP* php)
{
	assert(php);

	return php->size == 0;
}

HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	return php->data[php->size - 1];
}

3.2.3 堆的数据插入——向上调整

        对于一个堆,当我们插入一个数据的时候,这个数据会被插入到数组的尾部,也即二叉树的下一个结点处。但是我们的堆可不是那么随便的结构,小堆大堆数据之间有着自己特定的结构,这样直接在后面插入数据有可能会破坏其原有的结构,所以我们需要考虑如何再插入数据后保持其堆的特性。为此我们引入了向上调整算法

        向上调整算法目的是将新入堆的元素位置与原堆中的元素位置进行调整,使得再次构成一个堆。

        对于小堆而言,向上调整就是将子结点与其父亲相比,如果孩子小于父亲,则将二者位置调换,循环往复直到满足堆的条件为止。对于大堆而言,向上调整就是将子结点与其父亲相比,如果孩子大于父亲,则将二者位置调换,循环往复直到满足堆的条件为止。在调整过程中,每次交换后根据父子结点下标关系可以找到新的父结点与子结点。小堆和大堆的向上调整算法只在if语句中判断条件不同。

void AdjustUpMin(int* arr, int size)
{
	int child = size - 1;
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (arr[child] < arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void AdjustUpMax(int* arr, int size)
{
	int child = size - 1;
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (arr[child] > arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

        当有了向上调整算法后,我们就可以进行数据插入了。

void HeapPush(HP* php, HPDataType x)
{
	assert(php);

	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = realloc(php->data, sizeof(int) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		else
		{
			php->data = tmp;
		}
		php->capacity = newcapacity;
	}

	php->data[php->size] = x;
	php->size++;
	AdjustUp(php);
}

 3.2.4 堆的数据删除——向下调整

        对于堆来说,堆顶的元素是具有特殊意义的。小堆的堆顶是最小的,大队的堆顶是最大的。所以在对堆进行数据删除时,我们删除数组最后的元素意义不大,我们考虑的是取出堆顶元素后,针对元素堆顶进行删除。同理堆删除数据也会导致堆的结构被破坏,所以需要我们需要想办法恢复其堆的特性。这就需要我们设计向下调整算法

        向下调整算法的目的是将堆顶的元素与原堆中的元素交换位置,使得可以继续满足原来小堆或大堆的特征。

        对于小堆而言,向下调整是将其与子结点相比较,因为父结点需要小于子结点,所以我们需要选取左右孩子结点中较小的一个。为此我们可以假设左孩子是较小者,如果右孩子比左孩子小说明假设错误,让child+1即可。之后判断父结点与子结点的大小关系,如果满足小堆的要求就结束,否则将二者调换位置,然后找到新的父结点和子结点下标。如此循环,即可调整成为小堆。而对于大堆,和小堆同理,只是判断标准变成了父结点需要大于子节点,若父结点小于子节点则需要调整位置。

void AdjustDownMin(int* arr, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && arr[child] > arr[child + 1])
		{
			child++;
		}
		if (arr[child] < arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}

	}
}
void AdjustDownMax(int* arr, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && arr[child] < arr[child + 1])
		{
			child++;
		}
		if (arr[child] > arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

        于是乎,我们就可以写出数据删除的函数,这里是将堆顶元素删除,然后用最后一个元素代替堆顶元素,进行向下调整。文章来源地址https://www.toymoban.com/news/detail-806452.html

void HeapPop(HP* php)
{
	assert(php);

	Swap(&php->data[0], &php->data[php->size - 1]);
	php->size--;
	AdjustDown(php);
}

到了这里,关于数据结构——顺序二叉树——堆的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构】二叉树的顺序存储结构 —— 堆

    👑作者主页:@进击的安度因 🏠学习社区:进击的安度因(个人社区) 📖专栏链接:数据结构

    2023年04月08日
    浏览(45)
  • 【数据结构】二叉树的顺序结构及实现

    目录 1. 二叉树的顺序结构 2. 堆的概念及结构 3. 堆的实现 3.1 堆向下调整算法 3.2 堆的创建 3.3 建堆时间复杂度 3.4 堆的插入 3.5 堆的删除 3.6 堆的代码实现 4. 堆的应用 4.1 堆排序 4.2 TOP-K问题 普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉

    2024年02月08日
    浏览(42)
  • 【数据结构】树、二叉树的概念和二叉树的顺序结构及实现

    之前我们学习了顺序表、链表以及栈和队列这些数据结构,但这些数据结构都是线性的(一对一)。接下来要学习 非线性的数据结构——树(二叉树) ,相比前面的,树的结构更加复杂,话不多说,直接进入正题吧。 树是一种 非线性的数据结构 ,它是 一对多(也有可能是

    2024年02月07日
    浏览(42)
  • 数据结构<树和二叉树>顺序表存储二叉树实现堆排

    ✨Blog:🥰不会敲代码的小张:)🥰 🉑推荐专栏: C语言 🤪、 Cpp 😶‍🌫️、 数据结构初阶 💀 💽座右铭:“ 記住,每一天都是一個新的開始😁😁😁 ” 💀本章内容: 《树和二叉树》的介绍✨ 树是一种非线性的数据结构,它是由n(n=0)个有限结点组成一个具有层次关系

    2024年02月12日
    浏览(46)
  • 数据结构初阶--二叉树的顺序结构之堆

    目录 一.堆的概念及结构 1.1.堆的概念 1.2.堆的存储结构 二.堆的功能实现 2.1.堆的定义 2.2.堆的初始化 2.3.堆的销毁 2.4.堆的打印 2.5.堆的插入 向上调整算法 堆的插入 2.6.堆的删除 向下调整算法 堆的删除 2.7.堆的取堆顶元素 2.8.堆的判空 2.9.堆的求堆的大小 三.堆的创建 3.1.向上调

    2024年02月14日
    浏览(45)
  • 初阶数据结构之---二叉树的顺序结构-堆

    今天要讲的堆,不是操作系统虚拟进程地址空间中(malloc,realloc等开空间的位置)的那个堆,而是数据结构中的堆,它们虽然名字相同,却是截然不同的两个概念。堆的底层其实是 完全二叉树 ,如果你问我,完全二叉树是什么。好吧,那我先从树开始讲起,开始我们今天的

    2024年03月14日
    浏览(58)
  • 常见的数据结构(顺序表、顺序表、链表、栈、队列、二叉树)

    线性表(Linear List)     1.什么是线性表     2.线性表的特点     3.线性表的基本运算 顺序表     1.什么是顺序表     2.时间复杂度: 链表     1.什么是链表     2.单向链表     3. 双向链表     4.ArrayList和LinkedList的使用 栈Stack     1.什么是栈     2.栈的基本方法 队列Queue

    2024年02月13日
    浏览(38)
  • 二叉树的顺序结构以及堆的实现——【数据结构】

    W...Y的主页 😊 代码仓库分享  💕 上篇文章,我们认识了什么是树以及二叉树的基本内容、表示方法……接下来我们继续来深入二叉树,感受其中的魅力。 目录  二叉树的顺序结构 堆的概念及结构 堆的实现   堆的创建  堆的初始化与释放空间  堆的插入 堆的删除  堆实

    2024年02月07日
    浏览(47)
  • 数据结构——二叉树的基本概念及顺序存储(堆)

    目录 一.前言 二.树概念及结构 2.1 树的概念 2.2 树的相关概念 2.3 树的表现 2.4 树在实际中的应用(表示文件系统的目录树结构) 三.二叉树的概念及结构 3.1 概念 3.2 特殊的二叉树 3.3 二叉树的性质 3.4 二叉树的存储结构 3.4.1 顺序存储 3.4.2 链式存储 四.二叉树顺序结构及实现

    2024年02月08日
    浏览(45)
  • 【数据结构】二叉树的顺序结构实现及时间复杂度计算(二)

    目录 一,二叉树的顺序结构实现         1,二叉树的顺序结构         2,堆的概念及结构         3,堆的接口实现 1,堆的创建 2,接口函数 3,初始化 4,销毁 5,是否增容 6,交换数据 7,堆向上调整算法 8,插入数据 9,删除数据 10,堆向下调整算法 11,打印数

    2024年02月09日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包