【数据结构】堆的顺序结构及实现

目录

一,堆的顺序结构

二,堆的概念及结构

三,堆的实现

3.1堆的结构

3.2堆的初始化

3.3堆的插入数据

3.3堆的删除数据

3.4堆的取顶数据,返回堆数据大小,判断非空

3.5堆的销毁

四,总代码


一,堆的顺序结构

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

二,堆的概念及结构

如果有一个关键码的集合K = { k0,k1 ,k2 ,...,kn-1 },把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足: kn<=k2*n 且 kn<=k2n+2 (kn >=k2*n+1 且 kn>=k2*n+2 ) i = 0,1, 2...,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

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

三,堆的实现

因为堆分为大根堆和小根堆,结构不一样,但思路相同,这里我们以大根堆为例。

3.1堆的结构

这里用数组来实现,然后结构体中包含下标,容量。容量不够可以扩容。

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int HeapDataType;

typedef struct Heap
{
	HeapDataType* a;      //数组存储数据
	int size;    //最后一个元素的下标
	int capacity;//容量
}Heap;

3.2堆的初始化

这里申请一块数组的地址,申请失败则打印错误返回,然后将堆的指针指向数组的地址,将容量初始化为申请地址的大小,里面没有数据,将size置为0;

cpp 复制代码
void heapInit(Heap* pa)
{
	HeapDataType* tmp = (HeapDataType*)malloc(sizeof(HeapDataType)*4);
	if (tmp == NULL)
	{
		printf("malloc fail");
		return;
	}
	pa->a = tmp;
	pa->capacity = 4;
	pa->size = 0;
}

3.3堆的插入数据

这里涉及一个向上调整算法,它前提是数据的上面结点都为堆,然后插入大于2或者等于2后的数据都要与前面比较,如果是大根堆儿子比父亲大就交换(小根堆儿子节点比父亲小交换)数据,然后重复向上走,直到满足大根堆中父节点都比子节点大(小根堆父节点都比子节点小)后停止跳出循环。

cpp 复制代码
void swap(HeapDataType* p1, HeapDataType* p2)//两个数据互换
{
	HeapDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void AdjustUp(HeapDataType *a,int child)//child子节点的数组元素下标
{
	int parent = (child - 1) / 2;//二叉树中儿子结点与父亲结点的关系:父=(子-1)/2
	while (parent>=0)
	{
        //if(a[child]<a[parent])//小根堆
		if (a[child] > a[parent])//大根堆
		{
			swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

有了上面的向上调整算法,直接用在堆的插入中来建大根堆(小根堆)。先断言一下判断pa是否为空,不为空,继续执行。接着比较容量是否需要扩容,然后将数据插入进去,每进去一个数据都需要向上调整,以保证为大根堆(小根堆)。

cpp 复制代码
void heapPush(Heap* pa, HeapDataType x)
{
	assert(pa);
	if (pa->capacity == pa->size)
	{
		HeapDataType* tmp = (HeapDataType*)realloc(pa->a, sizeof(HeapDataType) * pa->capacity * 2);
		pa->a = tmp;
		pa->capacity *= 2;
	}
	pa->a[pa->size] = x;
	pa->size++;
	AdjustUp(pa->a, pa->size - 1);
}

3.3堆的删除数据

堆的删除数据不能直接将数组的第一个数据删除,否则堆的结构顺序将发生改变。这里我们可以将数组第一个数据与数组最后一个数据交换,然后将最后一个数据删除,如果在大根堆中此时第一个数据将比下面的小,但左右子树的结构不变(还是大根堆),这时候就从第一个数据开始想下调整,这里就涉及到向下调整的算法了,向下调整的算法前提是左右子树都是堆,这里先找到左右子结点中最大的结点(最小的结点),然后与子结点进行比较,如果符合条件就进行交换,不断重复上述过程,然后当子结点的数组下标大于等于数组的元素个数就结束。

cpp 复制代码
void swap(HeapDataType* p1, HeapDataType* p2)//两个数据交换
{
	HeapDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void AdjustDown(HeapDataType* a,int size,int parent)//向下调整
{
	int child = 2 * parent + 1;//二叉树中儿子结点与父亲结点的关系:父=(子-1)/2
	while (child < size)
	{

		if (child + 1 <= size && a[child] < a[child + 1])//child+1 元素的个数
		{
			child++;
		}
		if (a[child] > a[parent])
		{
			swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}

}
void heapPop(Heap* pa)
{
	assert(pa);//断言pa是否为空
	assert(!heapEmpty(pa));//断言pa中是否有数据
	swap(&pa->a[0],& pa->a[pa->size - 1]);//交换第一个与最后一个数据
	pa->size--;//删除的第一个数据(现在位于数组最后一个位置)
	AdjustDown(pa->a,pa->size-1,0);//pa->size 元素的个数,现在将位于数组第一个位置的数据向下调整
}

3.4堆的取顶数据,返回堆数据大小,判断非空

这些根据堆的结构中的size可以轻易取出,与之前一样,不再过多阐述。

cpp 复制代码
int heapEmpty(Heap* pa)//判断非空
{
	assert(pa);
	return pa->size == 0;
}
HeapDataType heapTop(Heap* pa)//取顶数据
{
	assert(pa);
	return pa->a[0];
}
int heapSize(Heap* pa)//取堆的数据大小
{
	assert(pa);
	return pa->size;
}

3.5堆的销毁

这里与其他销毁完全一样,不再叙述。

cpp 复制代码
void heapDestory(Heap* pa)
{
	assert(pa);
	free(pa->a);
	pa->size = 0;
	pa->capacity = 0;
}

四,总代码

cpp 复制代码
//heap.h
//存放头文件,接口声明
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int HeapDataType;

typedef struct Heap
{
	HeapDataType* a;
	int size;
	int capacity;
}Heap;
//以大根堆为例
void heapInit(Heap* pa);
void heapPush(Heap* pa,HeapDataType x);
void heapPop(Heap* pa);
int heapEmpty(Heap* pa);
HeapDataType heapTop(Heap* pa);
int heapSize(Heap* pa);
void heapDestory(Heap* pa);

//heap.c
//这里存放堆接口的实现
#define _CRT_SECURE_NO_WARNINGS 1
#include"heap.h"
void swap(HeapDataType* p1, HeapDataType* p2)
{
	HeapDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void AdjustDown(HeapDataType* a,int size,int parent)
{
	int child = 2 * parent + 1;
	while (child < size)
	{

		if (child + 1 <= size && a[child] < a[child + 1])//child+1 元素的个数
		{
			child++;
		}
		if (a[child] > a[parent])
		{
			swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}

}
void AdjustUp(HeapDataType *a,int child)
{
	int parent = (child - 1) / 2;
	while (parent>=0)
	{
		if (a[child] > a[parent])
		{
			swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void heapInit(Heap* pa)
{
	HeapDataType* tmp = (HeapDataType*)malloc(sizeof(HeapDataType)*4);
	if (tmp == NULL)
	{
		printf("malloc fail");
		return;
	}
	pa->a = tmp;
	pa->capacity = 4;
	pa->size = 0;
}
void heapPush(Heap* pa, HeapDataType x)
{
	assert(pa);
	if (pa->capacity == pa->size)
	{
		HeapDataType* tmp = (HeapDataType*)realloc(pa->a, sizeof(HeapDataType) * pa->capacity * 2);
		pa->a = tmp;
		pa->capacity *= 2;
	}
	pa->a[pa->size] = x;
	pa->size++;
	AdjustUp(pa->a, pa->size - 1);
}
void heapPop(Heap* pa)
{
	assert(pa);
	assert(!heapEmpty(pa));
	swap(&pa->a[0],& pa->a[pa->size - 1]);
	pa->size--;
	AdjustDown(pa->a,pa->size-1,0);//pa->size 元素的个数
}
int heapEmpty(Heap* pa)
{
	assert(pa);
	return pa->size == 0;
}
HeapDataType heapTop(Heap* pa)
{
	assert(pa);
	return pa->a[0];
}
int heapSize(Heap* pa)
{
	assert(pa);
	return pa->size;
}
void heapDestory(Heap* pa)
{
	assert(pa);
	free(pa->a);
	pa->size = 0;
	pa->capacity = 0;
}


//test.c
//这里存放堆的测试代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"heap.h"
void testpush()
{
	Heap hp;
	heapInit(&hp);
	heapPush(&hp, 12);
	heapPush(&hp, 21);
	heapPush(&hp, 14);
	heapPush(&hp, 51);
	heapPush(&hp, 11);
	heapPush(&hp, 23);
	while (!heapEmpty(&hp))
	{
		printf("%d\n", heapTop(&hp));
		heapPop(&hp);
	}
}
int main()
{
	testpush();
	return;
}

好了,到这里就结束了,下一篇来个效率更高的排序。

相关推荐
ChoSeitaku41 分钟前
链表交集相关算法题|AB链表公共元素生成链表C|AB链表交集存放于A|连续子序列|相交链表求交点位置(C)
数据结构·考研·链表
偷心编程42 分钟前
双向链表专题
数据结构
香菜大丸42 分钟前
链表的归并排序
数据结构·算法·链表
jrrz082842 分钟前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
@小博的博客1 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
泉崎3 小时前
11.7比赛总结
数据结构·算法
你好helloworld3 小时前
滑动窗口最大值
数据结构·算法·leetcode
JSU_曾是此间年少4 小时前
数据结构——线性表与链表
数据结构·c++·算法
sjsjs114 小时前
【数据结构-合法括号字符串】【hard】【拼多多面试题】力扣32. 最长有效括号
数据结构·leetcode
blammmp5 小时前
Java:数据结构-枚举
java·开发语言·数据结构