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

目录

前言

一、堆的实现

[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;
}

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


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

相关推荐
你好helloworld几秒前
滑动窗口最大值
数据结构·算法·leetcode
JSU_曾是此间年少1 小时前
数据结构——线性表与链表
数据结构·c++·算法
sjsjs111 小时前
【数据结构-合法括号字符串】【hard】【拼多多面试题】力扣32. 最长有效括号
数据结构·leetcode
blammmp2 小时前
Java:数据结构-枚举
java·开发语言·数据结构
昂子的博客3 小时前
基础数据结构——队列(链表实现)
数据结构
lulu_gh_yu3 小时前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
~yY…s<#>5 小时前
【刷题17】最小栈、栈的压入弹出、逆波兰表达式
c语言·数据结构·c++·算法·leetcode
XuanRanDev6 小时前
【每日一题】LeetCode - 三数之和
数据结构·算法·leetcode·1024程序员节
代码猪猪傻瓜coding6 小时前
力扣1 两数之和
数据结构·算法·leetcode
南宫生7 小时前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法