堆的创建插入删除排序(含堆的向上,向下调整)

堆再逻辑上可以看成一颗完全二叉树。
堆根据形状可以分为大根堆和小根堆。
故名思意,堆顶最小为小根堆,堆顶最大为大根堆,且根的其他子树也必须满足这一条件。
如图
小根堆:在这里插入图片描述
大根堆:
在这里插入图片描述
堆的常用结论:
对于一个具有n个节点的完全二叉树,如果按照从上到下从左至右的数组顺序对所有节点从 0 开始编号,则对于序号为 i 的节点有如下结论:

  1. 若 i > 0,i 节点的父节点为 ( i - 1)/2; i == 0 则 i 为根节点,无父节点。
  2. 若 2i + 1 < n, 左孩子节点为 2i + 1, 2i + 1 >= n 则无左孩子。
  3. 若 2i + 2 < n,右孩子节点为 2i + 2,2i + 2 >= n 则无右孩子。

注意点:
降序建立小根堆,然后交换堆顶尾元素并刨去尾部元素,再进行向下调整建立小堆。
升序建立大根堆,然后交换堆顶尾元素并刨去尾部元素,再进行向上调整建立大堆。
堆结构体:

typedef int HpDataType;
typedef struct Heap
{
	HpDataType* _a;
	size_t      _size;
	size_t      _capacity;
}Heap;

堆的向下调整算法
条件:左右子树都必须是堆

void AdjustDown(HpDataType* a, size_t n, int root)
{
	int parent = root;
	int child = parent * 2 + 1;
	while (child < n)
	{
		// 找出小的那个孩纸
		if (child + 1 < n && a[child+1] < a[child])  //1处 改成 a[child + 1] > a[child]
		{
			++child;
		}

		// 1、孩纸比父亲小,则交换,继续往下调
		// 2、孩纸比父亲大,则终止调整
		if (a[parent] > a[child])  //此时是调小堆,若改成 < 并且改 1 处 则为调大堆
		{
			Swap(&a[child], &a[parent]);  

			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}

向上调整算法:
同上,可调大堆也可调小堆,下面的代码是调小堆。

void AdjustUp(int* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			break;
	}
}

堆的建立:
我们输入的数值不一定满足每次子树都为堆的情况,此时就需要创建堆。

void HeapCreate(Heap* hp, HpDataType* a, size_t n)
{
	hp->_a = (HpDataType*)malloc(sizeof(HpDataType)*n);
	memcpy(hp->_a, a, sizeof(HpDataType)*n); //将数组a 中的元素copy到 hp->_a
	hp->_size = n;
	hp->_capacity = n;

	// 建小堆
	for (int i = (n-1-1)/2; i >= 0; --i)
	{  //循环遍历每一个父亲节点,进行调整
		AdjustDown(hp->_a, hp->_size, i);
    }

堆的插入:
原理是先把堆调好,然后将新数值插入到堆尾,然后进行向上排序。

void HeapPush(Heap* hp, HpDataType x)
{
	// 空间不够->增容
	if (hp->_size == hp->_capacity)
	{
		size_t newcapacity = hp->_capacity * 2;
		hp->_a = (HpDataType*)realloc(hp->_a, sizeof(HpDataType)*newcapacity);
		hp->_capacity = newcapacity;
	}

	hp->_a[hp->_size] = x;
	hp->_size++;

	// 向上调整,调成堆
	AdjustUp(hp->_a, hp->_size - 1);
}

堆的删除:
因为堆删除的是堆顶元素,所以首先交换堆顶和堆尾元素,再刨去堆尾元素(其实是交换过来的堆顶元素),然后进行向下排序。

void HeapPop(Heap* hp)
{
	Swap(&hp->_a[0], &hp->_a[hp->_size - 1]);
	hp->_size--;
	AdjustDown(hp->_a, hp->_size, 0);
}

完整的代码:

Heap_.h:

#include <stdio.h>
#include <string.h>
#include <malloc.h>

// 小堆
typedef int HpDataType;
typedef struct Heap
{
	HpDataType* _a;
	size_t      _size;
	size_t      _capacity;
}Heap;
//向下调整算法
void AdjustDown(HpDataType* a, size_t n, int root);
//创堆
void HeapCreate(Heap* hp, HpDataType* a, size_t n);
//插入
void HeapPush(Heap* hp, HpDataType x);
//删除
void HeapPop(Heap* hp);
//输出堆顶元素
HpDataType HeapTop(Heap* hp);
//判空
int HeapEmpty(Heap* hp);
//打印
void HeapPrint(Heap* hp);
// 最大十个数
void PrintTopK(int* a, int n, int k);
//向上调整算法
void AdjustUp(int* a, int child);
//销毁堆
void HeapDestory(Heap* hp);
//交换
void Swap(int* a, int* b);

Heap_.c

#include "Heap_.h"

void Swap(int* a, int* b)
{
	int x = *a;
	*a = *b;
	*b = x;
}

// 小堆 
// ->条件:左右子树都是堆
void AdjustDown(HpDataType* a, size_t n, int root)
{
	int parent = root;
	int child = parent * 2 + 1;
	while (child < n)
	{
		// 找出小的那个孩纸
		if (child + 1 < n && a[child+1] < a[child])
		{
			++child;
		}

		// 1、孩纸比父亲小,则交换,继续往下调
		// 2、孩纸比父亲大,则终止调整
		if (a[parent] > a[child])
		{
			Swap(&a[child], &a[parent]);

			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void HeapCreate(Heap* hp, HpDataType* a, size_t n)
{
	hp->_a = (HpDataType*)malloc(sizeof(HpDataType)*n);
	memcpy(hp->_a, a, sizeof(HpDataType)*n);
	hp->_size = n;
	hp->_capacity = n;

	// 建小堆
	for (int i = (n-1-1)/2; i >= 0; --i)
	{
		AdjustDown(hp->_a, hp->_size, i);
	}
}

void HeapDestory(Heap* hp)
{
	free(hp->_a);
	hp->_size = hp->_capacity = 0;
	hp->_a = NULL;
}

void AdjustUp(int* a, int child)
{
	int parent = (child - 1) / 2;
	//while (parent >= 0) // 没起作用,巧合借助break跳出的
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void HeapPush(Heap* hp, HpDataType x)
{
	// 空间不够-> 增容
	if (hp->_size == hp->_capacity)
	{
		size_t newcapacity = hp->_capacity * 2;
		hp->_a = (HpDataType*)realloc(hp->_a, sizeof(HpDataType)*newcapacity);
		hp->_capacity = newcapacity;
	}

	hp->_a[hp->_size] = x;
	hp->_size++;

	// 向上调整,调成堆
	AdjustUp(hp->_a, hp->_size - 1);
}

void HeapPop(Heap* 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 HeapEmpty(Heap* hp)
{
	return hp->_size == 0 ? 1 : 0;
}

void HeapPrint(Heap* hp)
{
	for (int i = 0; i < hp->_size; ++i)
	{ 
		printf("%d ", hp->_a[i]);
	}
	printf("\n");
}

// 最大十个数
void PrintTopK(int* a, int n, int k)
{
	// k个数的小堆
	Heap hp;
	HeapCreate(&hp, a, k);

	for (int i = k; i < n; ++i)
	{
		// 比堆顶的数据要大,就替代它
		if (HeapTop(&hp) < a[i])
		{
			HeapPop(&hp);
			HeapPush(&hp, a[i]);
		}
	}
	HeapPrint(&hp);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值