IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【数据结构】二叉树之堆的实现 -> 正文阅读

[数据结构与算法]【数据结构】二叉树之堆的实现

目录

一,树概念及结构

二,二叉树概念及结构?

特殊的二叉树:

二叉树的性质:

二叉树的存储结构:

三,二叉树的顺序结构及实现

二叉树的顺序结构:

堆的概念及结构:

堆的实现:

建堆:

堆向下调整算法:

堆排序:

建堆的时间复杂度:

四,堆的实现:

堆的初始化

堆的打印

堆的销毁

堆的插入

判空

删除堆顶的数据

获取堆顶的数据,也就是最大值,并计算堆的元素个数

?

具体代码:Gitee堆的实现

五,TopK问题


一,树概念及结构

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

特点:
·有一个特殊的结点,称为根结点,根节点没有前驱结点。
·除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i<= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继,因此,树是递归定义的。

二,二叉树概念及结构?

特殊的二叉树:

1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是 ,则它就是满二叉树。
2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

完全二叉树的节点个数范围最多:2^k-1,最少:2^(k-1)-1+1 = 2^(k-1)?

二叉树的性质:

1. 若规定根节点的层数为1,则一棵非空二叉树的第 i 层上最多有 2^(i-1)个结点.
2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h-1?
3. 对任何一棵二叉树, 如果度为0其叶结点个数为n0 , 度为2的分支结点个数为n2,则有 n0=n2+1
4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=log2(n+1)??

二叉树的存储结构:

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。

这里目前所学的知识有限,本文只列出了顺序结构:

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

物理公式:下标表示树中父子关系公式:

leftchild = parent*2+1

rightchild = parent*2+2?

parent = (child-1)/2

三,二叉树的顺序结构及实现

二叉树的顺序结构:

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段

堆的概念及结构:

小根堆(根是最小值):树中所有父亲都小于等于孩子

大根堆(根是最大值):树中所有父亲都大于等于孩子?

堆的实现:

建堆:

给了一个随机的数组,我们可以把这个数组看做完全二叉树,思考如果变成堆(大堆/小堆)

思考:如何将其变为大堆/小堆?

?堆向下调整算法:

?特殊数据的特征:根下面左右子树都是小堆/大堆

堆排序:

代码实现:

void swap(int* px,int* py)
{
	int tmp = *px;
	*px = *py;
	*py = tmp;
}
//向下调整算法(建大堆)
void AdjustDown(int* 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;
		}
	}
}
//堆排序——高效率
void HeapSort(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a,n,i);
	}
	int end = n - 1;
	while (end > 0)
	{
		swap(&a[0], &a[end]);
		AdjustDown(a,end,0);
		end--;
	}
}
int main()
{
	int a[] = { 27, 15, 19, 18, 28, 34, 65, 49, 25, 37 };
	HeapSort(a,sizeof(a)/sizeof(int));
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		printf(" %d",a[i]);
	}
	return 0;
}

建堆的时间复杂度:

注意点:

第一点:倒着走,第一个非叶子节点的子树开始调,它是最后一个节点的父亲

第二点:堆排序,将最大的数和最后一个数交换,再向下调整,时间复杂度O(N*logN)

四,堆的实现:

?堆的初始化

先初始化为数组a,然后在建堆(这里建大堆)

void HeapInit(Hp* php, HPDataType* a, int n)
{
	assert(php);
	php->a = (HPDataType*)malloc(sizeof(HPDataType)*n);
	if (php->a == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	memcpy(php->a, a, sizeof(HPDataType)*n);

	//建堆
	for (int i = (n - 1) / 2; i >= 0; i--)
	{
		AdjustDown(php->a, n, i);
	}
	php->size = n;
	php->capacity = n;
}

初始化调试:?

堆的打印

void HeapPrint(Hp*php)
{
	for (int i = 0; i < php->size; i++)
	{
		printf(" %d", php->a[i]);
	}
	printf("\n");
}

堆的销毁

void HeapDestroy(Hp* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->size = php->capacity = 0;
}

堆的插入

插入x,首先要先判断是不是满了,满了就需要扩容!

其次,还要继续保持其实堆,这里采用向上调整法

向上调整:?

//向上调整算法:
void AdjustUp(int* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[parent] < a[child])
		{
			swap(&a[parent], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void HeapPush(Hp* php, HPDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		HPDataType*tmp = (HPDataType*)realloc(php->a, php->capacity * 2 * sizeof(HPDataType));
		if (php->a == NULL)
		{
			printf("realloc fail");
			exit(-1);
		}
		php->capacity = (php->capacity) * 2;
	}

	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}

判空

bool HeapEmpty(Hp* php)
{
	assert(php);
	return php->size == 0;
}

删除堆顶的数据

这里删除堆顶的数据,要先判空,然后删除后还继续保持其是大堆

//删除堆顶即根节点的数据,删除后保持其继续是堆
void HeapPop(Hp* php)
{
	assert(php);
	assert(!HeapEmpty(php));
	swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;
	AdjustDown(php->a, php->size,0);
}

获取堆顶的数据,也就是最大值,并计算堆的元素个数

//获取堆顶的数据,也就是最值
HPDataType HeapTop(Hp* php)
{
	assert(php);
	assert(!HeapEmpty(php));
	return php->a[0];
}
int HeapSize(Hp* php)
{
	assert(php);
	return php->size;
}

运行测试:?

具体代码:Gitee堆的实现

五,TopK问题

思路:

void PrintTopK(int*a, int n, int k)
{
	Hp hp;
	HeapInit(&hp,a,k);
	for (int i = k; i < n; i++)
	{
		if (a[i]>HeapTop(&hp))
		{
			HeapPop(&hp);
			HeapPush(&hp,a[i]);
		}
	}
	HeapPrint(&hp);
	HeapDestroy(&hp);
}

思考:n个数的数组a——如何用向上调整建堆?

for (int i = 1; i < n; i++)
{
	adjustup(a,i);
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-09-06 11:24:08  更:2021-09-06 11:25:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年2日历 -2025/2/24 7:38:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码