数据结构-堆的构建及增删改

堆的定义

如果一串存储在数组中的数据,以完全二叉树的形式能形成
k[i]>k[2i+1] && k[i]>k[2i+2] 或者
k[i]<k[2i+1] && k[i]<k[2i+2] 前者称为大堆 后者称为小堆

在这里插入图片描述

堆的数据是储存在一维数组中来实现的。
所以说对于堆的每个数据下标都是有规律的。

对于一个下标为N的节点来说
它的父节点的下标为 (N-1)/2

一个下标为N父节点可能会有两个子节点
左节点为(N*2)+1
右节点为(N*2)+2

堆的实现(大堆)

我们要实现堆,首先要明确我们要使用啥结构来实现。
通过前面的定义我们知道
堆是存储在一维数组中的数据
所以顺序表成为了我们实现堆的不二之选

创建堆(顺序表)

//顺序表的结构体创建
typedef int datatype;
typedef struct 
{
	datatype* data;
	int size;
	int capacity;
}heap;
//顺序表定义
heap* HPinit()
{
	heap* HP=(heap*)malloc(sizeof(heap));
	assert(HP);
	datatype* arr=malloc(sizeof(datatype)*10);
	assert(arr);
	HP->data = arr;
	HP->size = 0;
	HP->capacity = 10;
	return HP;
}

插入数据

前面的数据插入和顺序表别无二致

void HPpush(heap* HP,datatype x)
{
	assert(HP);
	if (HP->capacity == HP->size)
	{
		datatype* arr = (datatype*)realloc(HP->data,sizeof(datatype)*HP->capacity*2);
		assert(arr);
		HP->data = arr;
		HP->capacity *= 2;
	}
	HP->data[HP->size] = x;
	HP->size++;
	adjustup(HP,HP->size-1);
}

但是我们在插入一个数据后,要将其进行向上调整,使其符合大根堆的规律。
所以我们要在插入函数的最后,添加一个adjustup函数

向上调整

基本思路

在这里插入图片描述

由前面的父系节点和孩子节点的下标的关系我们就能写出代码了。

//交换函数
void swap(heap* HP, int child, int parent)
{
	assert(HP);
	int tmp = HP->data[child];
	HP->data[child]=HP->data[parent];
	HP->data[parent] = tmp;
}
//调整函数
void adjustup(heap* HP, int position)
{
	int child = position;
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (HP->data[child] > HP->data[parent])
		{
		//判断大于父系节点时交换
			swap(HP, child, parent);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		//小于父系节点,表示已到合适位置,故跳出循环
			break;
	}
}

删除数据

不同的数据结构,我们需要考虑他们不同的作用
对于队列和栈他们删除数据都要遵从他们的结构性质

对于堆来说,最有意义的数据是堆顶的数据。
对于大根堆来说,堆顶的数据是最大数
对于小根堆来说,堆顶的数据是最小数

所以我们删除堆的数据函数,都是删除堆顶的数据。

明白了这点以后,我们就可以来看具体实现的步骤了。

向下调整

当我们删除头部数据以后,就需要进行向下调整。

因为我们不能直接进行数据的删除,如果直接将数据删除移除,这样就破坏了原本的数据结构

void HPPOP(heap* HP)
{
	assert(HP);
	if (Heapempty(HP))
	{
		return;
	}
	else
	{
		swap(HP, HP->size - 1, 0);
		HP->size--;//size自减,就代表删除了一个数据
		adjustdown(HP);//交换完向下调整
	}
}

在这里插入图片描述

这个时候就需要用-交换删除法并且结合向下调整
在这里插入图片描述
大致思路有了
接下来就是代码实现了

void adjustdown(heap* HP)
{
	assert(HP);
	int parent = 0;
	int child = parent*2+1;
	while (child< HP->size - 1)
	//当前下标大于数组中最大下标停止
	{
		if (HP->data[child] < HP->data[child + 1]&& child+1<HP->size-1)
		//假设child为左孩子节点,与右孩子进行比较,如果右孩子大,则交换
		{
			child += 1;
		}
		
		if (HP->data[parent] < HP->data[child])
		{
		//父节点小于孩子节点交换
			swap(HP, parent, child);
			parent = child;
			child = (child * 2) + 1;
		}
		else
		//否则退出循环
			break;
	}
}

接下来都是顺序表的经典操作了,就直接上代码了。

返回堆顶元素

datatype HPtop(heap* HP)
{
	assert(HP);
	return HP->data[0];
}

判断堆是否为空

bool Heapempty(heap* HP)
{
	assert(HP);
	return (!HP->size);
}

整体代码

头文件部分

#pragma once

#include<stdlib.h>
#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
typedef int datatype;
typedef struct 
{
	datatype* data;
	int size;
	int capacity;
}heap;

heap* HPinit();
void adjustup(heap* HP,int position);
void HPpush(heap*HP,datatype x);
void HPPOP(heap* HP);
void adjustdown(heap* HP);
void swap(heap* HP, int child, int parent);
bool Heapempty(heap* HP);
datatype HPtop(heap* HP);

函数实现部分

#include"Heap.h"
heap* HPinit()
{
	heap* HP=(heap*)malloc(sizeof(heap));
	assert(HP);
	datatype* arr=malloc(sizeof(datatype)*10);
	assert(arr);
	HP->data = arr;
	HP->size = 0;
	HP->capacity = 10;
	return HP;
}
void swap(heap* HP, int child, int parent)
{
	assert(HP);
	int tmp = HP->data[child];
	HP->data[child]=HP->data[parent];
	HP->data[parent] = tmp;
}
void adjustup(heap* HP, int position)
{
	int child = position;
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (HP->data[child] > HP->data[parent])
		{
			swap(HP, child, parent);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			break;
	}
}

void HPpush(heap* HP,datatype x)
{
	assert(HP);
	if (HP->capacity == HP->size)
	{
		datatype* arr = (datatype*)realloc(HP->data,sizeof(datatype)*HP->capacity*2);
		assert(arr);
		HP->data = arr;
		HP->capacity *= 2;
	}
	HP->data[HP->size] = x;
	HP->size++;
	adjustup(HP,HP->size-1);
}
void adjustdown(heap* HP)
{
	assert(HP);
	int parent = 0;
	int child = parent*2+1;
	while (child<= HP->size - 1)
	{
		if (HP->data[child] < HP->data[child + 1]&& child+1<HP->size-1)
		{
			child += 1;
		}
		if (HP->data[parent] < HP->data[child])
		{
			swap(HP, parent, child);
			parent = child;
			child = (child * 2) + 1;
		}
		else
			break;
	}
}
void HPPOP(heap* HP)
{
	assert(HP);
	if (Heapempty(HP))
	{
		return;
	}
	else
	{
		swap(HP, HP->size - 1, 0);
		HP->size--;
		adjustdown(HP);
	}
}

datatype HPtop(heap* HP)
{
	assert(HP);
	return HP->data[0];
}

bool Heapempty(heap* HP)
{
	assert(HP);
	return (!HP->size);
}

测试部分(用户自己随便引用)

#include"Heap.h"
int main()
{
	heap* HP = HPinit();
	HPpush(HP, 1);
	HPpush(HP, 2);
	HPpush(HP, 3);
	HPpush(HP, 4);
	HPpush(HP, 5);
	HPpush(HP, 6);
	HPpush(HP, 23);
	HPpush(HP, 223);
	HPpush(HP, 423);
	HPPOP(HP);
	while (!Heapempty(HP))
	{
		printf("%d ", HP->data[0]);
		HPPOP(HP);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想学c啊啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值