数据结构:顺序二叉树(堆)

目录

前言

一、堆的实现

[1.1 头文件](#1.1 头文件)

[1.2 堆的初始化及销毁](#1.2 堆的初始化及销毁)

[1.3 堆的插入](#1.3 堆的插入)

[1.4 堆的删除](#1.4 堆的删除)

[1.5 取堆顶数据和判空](#1.5 取堆顶数据和判空)


前言

前面我们讲了二叉树有顺序结构和链式结构,今天就来讲一下顺序结构

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

一、堆的实现

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

1.1 头文件

复制代码
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}Heap;

//堆的初始化
void HeapInit(Heap*hp);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的判空
bool HeapEmpty(Heap* hp);

写入我们需要用到的头文件以及堆的基础结构还有用到的堆的功能函数名。

1.2 堆的初始化及销毁

复制代码
void HeapInit(Heap* hp)
{
	assert(hp);
	hp->a = NULL;
	hp->size = hp->capacity = 0;
}
void HeapDestory(Heap* hp)
{
	assert(hp);
	free(hp->a);
	hp->a = NULL;
	hp->size = hp->capacity = 0;
}

这个没什么还说的,就是把数据都置空了。

1.3 堆的插入

复制代码
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
	if (hp->size == hp->capacity)
	{
		int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		HPDataType* tem = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);
		hp->capacity = newcapacity;
		hp->a = tem;
	}
	hp->a[hp->size] = x;
	hp->size++;
	adjustup(hp->a, hp->size-1);
}

这里我们会用到向上调整算法

复制代码
void swap(HPDataType* a, HPDataType* b)
{
	HPDataType tem = *a;
	*a = *b;
	*b = tem;
}
void adjustup(HPDataType* a, HPDataType 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;
		}
	}
}

插入数据后要保持父节点比子节点小的性质,要把数据一步步向上调整。

1.4 堆的删除

复制代码
void HeapPop(Heap* hp)
{
	assert(hp);
	swap(&hp->a[0], &hp->a[hp->size - 1]);
	hp->size--;
	adjustdown(hp->a, hp->size,0);
}

删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。

复制代码
void adjustdown(HPDataType* a, HPDataType n,HPDataType parent)
{
	HPDataType 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;
		}
	}
}

这里给个例子:

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根结点开始的向下调整算法可以把它调整 成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

int array\[\] = {27,15,19,18,28,34,65,49,25,37};

1.5 取堆顶数据和判空

复制代码
HPDataType HeapTop(Heap* hp)
{
	return hp->a[0];
}

因为是基于数组写的,所以取堆顶就很简单了。

复制代码
bool HeapEmpty(Heap* hp)
{
	return hp->size == 0;
}

如果数据位空了,那么堆就是空的。

二、完整源码

dui.h

复制代码
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}Heap;


void HeapInit(Heap*hp);

void HeapDestory(Heap* hp);

void HeapPush(Heap* hp, HPDataType x);

void HeapPop(Heap* hp);

HPDataType HeapTop(Heap* hp);

int HeapSize(Heap* hp);

bool HeapEmpty(Heap* hp);

dui.c

复制代码
#include"dui.h"
void HeapInit(Heap* hp)
{
	assert(hp);
	hp->a = NULL;
	hp->size = hp->capacity = 0;
}
void HeapDestory(Heap* hp)
{
	assert(hp);
	free(hp->a);
	hp->a = NULL;
	hp->size = hp->capacity = 0;
}
void swap(HPDataType* a, HPDataType* b)
{
	HPDataType tem = *a;
	*a = *b;
	*b = tem;
}
void adjustup(HPDataType* a, HPDataType 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(Heap* hp, HPDataType x)
{
	assert(hp);
	if (hp->size == hp->capacity)
	{
		int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		HPDataType* tem = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);
		hp->capacity = newcapacity;
		hp->a = tem;
	}
	hp->a[hp->size] = x;
	hp->size++;
	adjustup(hp->a, hp->size-1);
}
void adjustdown(HPDataType* a, HPDataType n,HPDataType parent)
{
	HPDataType 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 HeapPop(Heap* hp)
{
	assert(hp);
	swap(&hp->a[0], &hp->a[hp->size - 1]);
	hp->size--;
	adjustdown(hp->a, hp->size,0);
}
HPDataType HeapTop(Heap* hp)
{
	return hp->a[0];
}
int HeapSize(Heap* hp)
{
	return hp->size;
}
bool HeapEmpty(Heap* hp)
{
	return hp->size == 0;
}

还是要自己动手写一边才能有印象,写完每个功能点最好一个一个的去测试,这样就不会那么麻烦,更容易知道错哪了,全部写写完再测试,没错误还好说,有错的话,找起来会很痛苦的。


本篇内容就到这里了,希望对各位有帮助,如果有错误欢迎指出。

相关推荐
CSharp精选营4 天前
关系型 vs 非关系型:从原理到选型,一文搞定数据库核心分类
数据结构·nosql·关系型数据库·非关系型数据库·技术选型
刘马想放假7 天前
Modbus 全栈技术解析:TCP、RTU、ASCII、RTU over TCP
数据结构·网络协议
北域码匠8 天前
冒泡排序太慢?鸡尾酒排序双向优化,原生 C# 零第三方库完整代码
数据结构·排序算法·泛型·c# 算法·鸡尾酒排序·原生 c# 开发·冒泡排序优化·嵌入式算法
Darling噜啦啦15 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
小小工匠16 天前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
玖玥拾16 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
Qres82116 天前
算法复键——树状数组
数据结构·算法
牛油果子哥q16 天前
并查集(DSU)超精讲,路径压缩、按秩合并、万能模板、连通性判定、最小生成树与刷题实战全解
数据结构·c++·最小生成树·并查集
凌波粒16 天前
LeetCode--491.递增子序列(回溯算法)
数据结构·算法·leetcode
WL学习笔记16 天前
单项不带头不循环链表
数据结构·链表