Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >【数据结构】二叉查找树和二叉堆

【数据结构】二叉查找树和二叉堆

作者头像
薄荷冰
发布于 2024-01-22 10:24:57
发布于 2024-01-22 10:24:57
24200
代码可运行
举报
文章被收录于专栏:后端学习之旅后端学习之旅
运行总次数:0
代码可运行

下面让我们从二叉树的应用讲起。

1.二叉树的查找

二叉树的树形结构使它很适合扮演索引的角色。

这里我们介绍一种特殊的二叉树:二叉查找树(binary search tree) 。光看名字就可以知道,这种二叉树的主要作用就是进行查找 操作。

二叉查找树在二叉树的基础上增加了以下几个条件。

  • 如果左子树不为空,则左子树上所有节点的值均小于根节点的值
  • 如果右子树不为空,则右子树上所有节点的值均大于根节点的值
  • 左、右子树也都是二叉查找树

下图就是一个标准的二叉查找树

这样在从二叉树中查找一个数时,时间复杂度可以达到

2.二叉堆

比大小一直是编程种很重要的一环,但有时候我们并不需要知道所有的排名,而是只要一小部分。及TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。这时我们通常采用堆(一种二叉树)来解决这种问题。 需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

2.1堆的概念及其结构

如果有一个关键码的集合K = {

,…,

},把它的所有元素按完全二叉树的顺序存储方式存储

在一个一维数组中,并满足: <= 且 <= ( >= 且 >= ) i = 0,1, 2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:

  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树

根据大根和小根我们将堆分为大堆小堆

2.2堆的实现

这里我们首先介绍堆的向下调整。所谓向下调整就是从根节点开始的向下调整算法可以把它调整 成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。简单来说,向下调整就是将根节点和左右孩子节点进行比较若他比左右孩子中的任一数大就进行替换(小堆),若都小就和左孩子进行替换。与他相似的向上调整就是从最后一个数据与他的父节点进行比较使二叉树最后成为堆,他的前提条件是除该数据外其余数据构成堆。

注意:无论是向上调整还是向下调整,其目的都是形成一个堆,所以除该数据外其余数据构成堆。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//交换位置
void Swap(HPDataType* a, HPDataType* b)
{
	HPDataType tmp = *a;
	*a = *b;
	*b = tmp;
}
//向上调整
void AdjustUp(HPDataType* a,int child)
{
    int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
//向下调整
void Adjustdown(HPDataType* a, int n,int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		// 选出左右孩子中大的那一个
		if (child + 1 < n && a[child + 1] > a[child])//确保右孩子不越界
		{
			++child;
		}

		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
2.3堆的创建

如果给定一个数组这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算

法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的 子树开始调整,一直调整到根节点的树,就可以调整成堆。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int a[] = {1,5,3,8,7,6};
堆的基本操作(插入,删除)

插入就直接将插入的数据放到数组的最后处,然后对他进行向上调整。

删除(特指删除根节点)可以将数组最后的数将根节点覆盖,然后对根节点进行向下调整。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef int HPDataType;
typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity;
}Heap;

// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
	//扩容

	if (hp->_capacity == hp->_size)
	{
		HPDataType* tmp =(HPDataType*) realloc(hp->_a, sizeof(HPDataType) * hp->_capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc");
			return;
		}
		hp->_capacity *= 2;
		hp->_a = tmp;
	}
	hp->_a[hp->_size] = x;
	Adjustup(hp->_a, hp->_size);
	hp->_size++;

}
// 堆的删除
void HeapPop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	Swap(&hp->_a[0], &hp->_a[hp->_size - 1]);
	hp->_size--;
	Adjustdown(hp->_a, hp->_size, 0);
}
2.4堆的插入、删除、构建操 作的时间复杂度

堆的插入操作是单一节点的“上浮”,堆的删 除操作是单一节点的“下沉”,这两个操作的平均交换次数都是 堆高度的一半,所以时间复杂度是O(logn)。而构建堆的时间复杂度是O(n)推导如下。

3.二叉堆的应用

3.1堆排序

堆排序即利用堆的思想来进行排序,总共分为两个步骤:

1. 建堆

升序:建大堆

降序:建小堆

2. 利用堆删除思想来进行排序

建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void HeapSort(int* a, int n)
{

	// 建堆 -- 向下调整建堆 -- O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[end], &a[0]);
		AdjustDown(a, end, 0);

		--end;
	}
}
3.2TOP-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大

比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。

对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能

数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:

1. 用数据集合中前K个元素来建堆

前k个最大的元素,则建小堆

前k个最小的元素,则建大堆

2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void PrintTopK(const char* file, int k)
{
	// 1. 建堆--用a中前k个元素建小堆
	int* topk = (int*)malloc(sizeof(int) * k);
	assert(topk);

	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error"); 
		return;
	}

	// 读出前k个数据建小堆
	for (int i = 0; i < k; ++i)
	{
		fscanf(fout, "%d", &topk[i]);
	}

	for (int i = (k - 2) / 2; i >= 0; --i)
	{
		Adjustdown(topk, k, i);
	}
	// 2. 将剩余n-k个元素依次与堆顶元素交换,不满则则替换
	int val = 0;
	int ret = fscanf(fout, "%d", &val);
	while (ret != EOF)
	{
		if (val > topk[0])
		{
			topk[0] = val;
			Adjustdown(topk, k, 0);
		}

		ret = fscanf(fout, "%d", &val);
	}
	for (int i = 0; i < k; i++)
	{
		printf("%d ", topk[i]);
	}
	printf("\n");

	free(topk);
	fclose(fout);

}
void CreateNDate()
{
	// 造数据
	int n = 10000000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}

	for (size_t i = 0; i < n; ++i)
	{
		int x = rand() % 10000;
		fprintf(fin, "%d\n", x);
	}

	fclose(fin);
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-01-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
数据结构之堆的结构与实现
我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。
用户10923276
2024/03/28
1130
数据结构之堆的结构与实现
【数据结构】二叉树---堆
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。 有一个特殊的结点,称为根结点,根节点没有前驱结点。除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继。因此,树是递归定义的。
YoungMLet
2024/03/01
1180
【数据结构】二叉树---堆
【数据结构】二叉树-堆(下)-链式二叉树
在排序当中,堆排序是一种时间复杂度较低的排序,要远优于冒泡排序,在使用堆排序时,要使用向下调整算法,这样我们就可以最大限度的减少时间的使用
s-little-monster
2024/06/06
850
【数据结构】二叉树-堆(下)-链式二叉树
【数据结构】堆与堆排序
普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆 ( 一种二叉树 ) 使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统 虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
用户11290673
2024/09/25
1000
【数据结构】堆与堆排序
【初阶数据结构】二叉树(附题)
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
ZLRRLZ
2024/12/13
3260
【初阶数据结构】二叉树(附题)
【数据结构】堆(万字详解)
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链,后面学到高阶数据结构如红黑树等会用到三叉链。
每天都要进步呀
2023/03/28
1.4K0
【数据结构】堆(万字详解)
二叉树由浅至深(上)
树有很多种表示方式,如:双亲表示法,孩子表示法、孩子兄弟表示法等等。这里简单了解其中最常用的孩子兄弟表示法。
海盗船长
2020/08/27
2860
【数据结构初阶】树+二叉树+堆的实现+堆的应用
树是一种非线性的数据结构,它是一种由有限个结点组成的具有层状结构的集合,把它叫做树是因为它看起来像一颗倒挂起来的树,叶子朝下,根root朝上。
举杯邀明月
2023/04/12
3570
【数据结构初阶】树+二叉树+堆的实现+堆的应用
堆的实现与堆排序
本篇旨在介绍二叉树中特殊结构堆, 以及堆的实现与应用 更多文章 博客主页: 酷酷学!!! 点击关注 一起加油~
用户11317877
2024/10/16
830
堆的实现与堆排序
【数据结构】二叉树与堆
树的概念:树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的
平凡的人1
2023/10/15
2330
【数据结构】二叉树与堆
[数据结构]二叉树的顺序存储结构
普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结 构存储。现实中我们通常把堆 ( 一种二叉树 ) 使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统 虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
IT编程爱好者
2023/04/12
4150
[数据结构]二叉树的顺序存储结构
【数据结构】堆/堆排序(含top-k问题)(调整方式)(简洁,含代码)
YY的秘密代码小屋
2024/01/22
2010
【数据结构】堆/堆排序(含top-k问题)(调整方式)(简洁,含代码)
【数据结构初阶】顺序结构二叉树(堆)接口实现超详解
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。有一个特殊的结点,称为根结点,根结点没有前驱结点。 除根结点外,其余结点被分成 M(M>0)个互不相交的集合 T1、T2、…、Tm ,其中每一个集合Ti (1 <= i<= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继。因此,树是递归定义的。 树形结构中,子树之间不能有交集,否则就不是树形结构。其实就是一个节点不能有多个前驱。 树可以看做是这样的:
fhvyxyci
2024/09/24
1310
【数据结构初阶】顺序结构二叉树(堆)接口实现超详解
把所有的元素按照完全二叉树的形式储存在一维数组中,如果该二叉树满足父节点小于等于子节点,叫做小堆;如果该二叉树满足父节点大于等于子节点,叫做大堆。
code-child
2023/05/30
2460
堆
二叉树详解(1)
叶节点或终端节点:度为0的节点称为叶节点; 如上图:B、C、H、I…等节点为叶节点(没有孩子的节点)
waves浪游
2024/08/17
990
二叉树详解(1)
数据结构——二叉树
1. 子树之间不能有交集(子树不能相交)如果存在相交,那么就是另外一种数据结构图。
用户11352420
2024/11/07
870
数据结构——二叉树
树和二叉树的基本概念和堆的实现
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
ahao
2024/03/19
980
树和二叉树的基本概念和堆的实现
【数据结构和算法】---二叉树(2)--堆的实现和应用
如果有一个数字集合,并把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,且在逻辑结构(即二叉树)中,如果每个父亲节点都大于它的孩子节点那么此堆可以称为大堆;那么如果每个父亲节点都小于它的孩子节点那么此堆可以称为小堆。 堆的性质:
用户11029269
2024/03/19
830
【数据结构和算法】---二叉树(2)--堆的实现和应用
【算法与数据结构】深入解析二叉树(二)之堆结构实现
普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
学习起来吧
2024/03/23
1060
【算法与数据结构】深入解析二叉树(二)之堆结构实现
【数据结构】堆的实现和堆排序--TOP-K问题
堆是一种特殊的树形数据结构,常用于实现优先队列和堆排序。它基于完全二叉树,通常用数组表示。主要操作包括插入(通过上滤维护堆性质)和删除(通常删除堆顶元素,通过下滤恢复堆性质)。
用户11375356
2024/11/22
790
【数据结构】堆的实现和堆排序--TOP-K问题
推荐阅读
相关推荐
数据结构之堆的结构与实现
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验