创建堆

news/2024/5/19 2:32:10 标签: 二叉树, 数据结构, 堆排序

第一次尝试

//这是 .h 部分的代码
#pragma once

//使用这种方式来重命名数据类型,这样可以很方便的修改后续数据的数据类型,相当于#define的作用
typedef int HeapType;

//创建堆结构体,也就是动态顺序表
typedef struct Heap
{
	//动态开辟数组有来存放数据
	HeapType* _date;
	//堆的有效元素个数
	int _size;
	//堆的容量
	int _capacity;
}Heap;

//所有函数的声明

//向下调整
void AdjustDown(HeapType* a, int n, int root);
//向上调整
void AdjustUp(HeapType* a, int n, int root);
//交换函数
void swap(HeapType* x, HeapType* y);
//堆的初始化
void HeapInit(Heap* hp);
// 堆的构建
void HeapCreate(Heap* hp, HeapType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
//检查容量
void CheckCapacity(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HeapType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HeapType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);
//打印函数
void HeapPrint(Heap* hp);
// 对数组进行堆排序
void HeapSort(HeapType* a, int n);
//打印n个数中,前k个最小的数
void PrintTopK(int* a, int n, int k);
//这是 .c 部分的代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include"heap.h"

//向下调整
void AdjustDown(HeapType* a, int n, int i) {
	//此函数是在其他函数内部调用的,所以此处不需检查
	//下面做循环,以建小堆为例;
	//从给定结点开始,如果该父结点比孩子结点中最小的大,那么就交换这两个结点,接着向下走,否则跳出循环
	//循环一直到最后一个非叶子节点
	while (i <= (n - 2) / 2) {
		int cur = 0;
		//下面的 if 是判断如果走到最后一个非叶子节点,那么有两种情况,那就是该父结点的度是 1 还是 2
		if (2 * i + 2 < n) {
			//如果有右孩子,那么就找出左右孩子中最小的,与其比较
			cur = (a[2 * i + 1] < a[2 * i + 2] ? 2 * i + 1 : 2 * i + 2);
		}
		else {
			//如果没有右孩子,那么就只和左孩子比较
			cur = 2 * i + 1;
		}
		//判断父结点和比孩子结点大小关系
		if (a[i] > a[cur]) {
			//父结点大于孩子结点,就交换
			swap(&a[i], &a[cur]);
			//更新节点位置
			i = cur;
		}
		else {
			//父结点比孩子结点小,就结束循环
			return;
		}
	}
}

//向上调整
void AdjustUp(HeapType* a, int n, int i) {
	//此函数是在其他函数内部调用的,所以此处不需检查
	//下面循环是以创建好的小堆来进行向上调整的
	while (i) {
		//比较当前位置元素和父结点元素大小
		if (a[i] < a[(i - 1) / 2]) {
			//如果父结点大于当前结点,那就交换
			swap(&a[i], &a[(i - 1) / 2]);
			//并更新待比较位置
			i = (i - 1) / 2;
		}
		//如果父结点小于当前节点就直接结束
		else {
			return;
		}
	}
}

//交换函数
void swap(HeapType* x, HeapType* y) {
	int temp = 0;
	temp = *x;
	*x = *y;
	*y = temp;
}

//堆的初始化
void HeapInit(Heap* hp) {
	//参数合法性检验
	if (hp == NULL) {
		return;
	}
	hp->_date = NULL;
	hp->_capacity = hp->_size = 0;
}

// 堆的构建,将已有数组构建成堆
void HeapCreate(Heap* hp, HeapType* a, int n) {
	//参数合法性检验
	if (hp == NULL) {
		return;
	}
	//动态开辟数组大小空间
	hp->_date = (HeapType*)malloc(sizeof(HeapType) * n);
	//循环将数据放入堆中
	for (int i = 0; i < n; i++) {
		hp->_date[i] = a[i];
	}
	hp->_capacity = hp->_size = n;
	//循环向下调整,使堆变成小堆
	for (int i = (n - 2) / 2; i >= 0; i--) {
		AdjustDown(hp->_date, n, i);
	}
}

// 堆的销毁
void HeapDestory(Heap* hp) {
	//参数合法性检验
	if (hp == NULL) {
		return;
	}
	//释放空间
	free(hp->_date);
	//令其等于NULL,防止成为野指针
	hp->_date == NULL;
}

//检查容量
void CheckCapacity(Heap* hp) {
	//此函数是在其他函数内部调用的,所以此处不需检查
	//有效元素等于容量,则说明满了,需要增容
	if (hp->_capacity == hp->_size) {
		hp->_capacity = hp->_capacity == 0 ? 1 : 2 * hp->_capacity;
		//使用 realloc 函数进行空间增容
		hp->_date = (HeapType*)realloc(hp->_date,sizeof(HeapType) * hp->_capacity);
	}
}

// 堆的插入;方法为先尾插入,再向上调整
void HeapPush(Heap* hp, HeapType x) {
	//参数合法性检验
	if (hp == NULL) {
		return;
	}
	//容量检查
	CheckCapacity(hp);
	//尾插
	hp->_date[hp->_size] = x;
	hp->_size++;
	//向上调整
	AdjustUp(hp->_date, hp->_size, hp->_size - 1);
}

// 堆的删除;方法为先将堆顶元素和堆尾元素互换,然后尾删,再堆顶向下调整
void HeapPop(Heap* hp){
	//参数合法性检验,如果为空堆,那么直接返回
	if (hp == NULL || hp->_size == 0) {
		return;
	}
	//堆顶堆尾元素互换
	swap(&hp->_date[0], &hp->_date[hp->_size - 1]);
	//尾删
	hp->_size--;
	//堆顶向下调整
	AdjustDown(hp->_date, hp->_size, 0);
}

// 取堆顶的数据
HeapType HeapTop(Heap* hp) {
	//此处无法进行参数合法性检验,因为没有任何值可以作为出错的返回值
	return hp->_date[0];
}

// 堆的数据个数
int HeapSize(Heap* hp) {
	//参数合法性检验
	if (hp == NULL || hp->_size == 0) {
		return 0;
	}
	return hp->_size;
}

// 堆的判空,为空返回非零值,非空返回零
int HeapEmpty(Heap* hp) {
	//参数合法性检验
	if (hp == NULL || hp->_size == 0) {
		return 1;
	}
	return 0;
}

//打印函数
void HeapPrint(Heap* hp) {
	//参数合法性检验
	if (hp == NULL) {
		return;
	}
	//循环打印
	for (int i = 0; i < hp->_size; i++) {
		printf("%d ", hp->_date[i]);
	}
	printf("\n");
}

// 对数组进行堆排序,降序排序(如果是小堆就排降序,如果是大堆就排升序,本代码中是小堆,所以排降序)
void HeapSort(HeapType* a, int n) {
	//对数组的数据进行向下调整,建成小堆
	for (int i = (n - 2) / 2; i >= 0; i--) {
		AdjustDown(a, n, i);
	}
	int cur = n;
	//进行循环,每次将对顶元素和当前堆的最后一个位置互换,然后堆的个数减一,然后进行向下调整重新变成小堆
	//上面的操作就是在将堆的最小值放到最后,然后对剩下的元素再次进行向下调整,这样循环下去
	while (--cur) {
		swap(&a[0], &a[cur]);
		AdjustDown(a, cur, 0);
	}
}

//打印n个数中,前k个最小的数
void PrintTopK(int* a, int n, int k) {
	Heap hp;
	HeapInit(&hp);
	//将数据插入堆中
	for (int i = 0; i < n; i++) {
		HeapPush(&hp, a[i]);
	}
	//拿堆顶元素,然后删除堆顶元素
	for (int i = 0; i < k; i++) {
		printf("%d\n", HeapTop(&hp));
		HeapPop(&hp);
	}
}

void TestTopk(){
	int n = 10000;
	int* a = (int*)malloc(sizeof(int) * n);
	srand(time(0));
	//随机生成10000个数存入数组,保证元素都小于1000000
	for (size_t i = 0; i < n; ++i)
	{
		a[i] = rand() % 1000000 + 11;
	}
	//确定10个最大的数
	a[5] = 1;
	a[1231] = 2;
	a[531] = 3;
	a[5121] = 4;
	a[115] = 5;
	a[2335] = 6;
	a[9999] = 7;
	a[76] = 7;
	a[423] = 9;
	a[3144] = 10;

	PrintTopK(a, n, 10);
}

int main() {
	TestTopk();
	return 0;
}

  本代码的功能是创建堆,使用的方法为顺序表创建;堆的结构是把它的所有元素按 完全二叉树 的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,我们称之为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
  创建大/小堆时,使用的方法为向下调整法,设要调整节点为 a,那么首先要保证 a 的子结构是大/小堆,然后向下调整,找到 a 的合适位置即可;那么要保证 a 的子结构也为大/小堆的话,就需要使用循环或者递归,从最后一个非叶子结点开始,调整元素的大小关系(按照大/小堆的概念来调整),调整结束后就可以调整 a 的位置了。
  本代码还实现了向堆中插入元素、删除堆中元素、获取堆顶元素等等基本操作的接口,这里就不一一赘述了,具体看代码中的注释,代码中的注释很详细,每一步是干什么都有详细注明。


http://www.niftyadmin.cn/n/1578985.html

相关文章

[ArcEngine]Geotransformation地理变换

Geotransformation 地理变换 The Abridged Molodensky transformation is a three parameter transformation三参 that converts between two geographic coordinate systems (datums)两个基准面. The parameters are three translations in meters. The translation values ar…

二叉树初阶

目录树的概念及结构概念结构树的专有名词树的表示与应用树的表示树的应用二叉树概念及结构概念结构二叉树性质二叉树的顺序结构的实现二叉树的顺序结构堆的概念及结构堆的实现二叉树链式结构的实现概念遍历实现二叉树算法题树的概念及结构 概念 树是一种非线性的数据结构&…

解决apache启动错误httpd:Could not reliably determine...

启动apache遇到错误&#xff1a;httpd: Could not reliably determine the servers fully qualified domain name[rootserver httpd-2.2.4]# /usr/local/apache/bin/apachectl starthttpd: Could not reliably determine the servers fully qualified domain name, using 127.0…

推翻了重来!!!

前几周和专业人士聊了会&#xff0c;感觉自己想通过API来让用户获取自己的比赛记录等信息实在是蠢。毕竟APP会因为官方API的原因受到很大的限制&#xff0c;转而希望在用户的体验上作出限制或控制&#xff0c;实在是逗B的不行。 结果之后就开始玩的不亦乐乎&#xff0c;好似不去…

2014第17周三

今天写了一个模块从前台到后台的整个逻辑&#xff0c;其中在form表单提交时遇到了问题&#xff0c;既想通过表单提交参数又想获取表单返回值&#xff0c;尝试很很久差ajaxsubmit解决了问题

用AUTOIT来管理升级分发公司设计图框及字体库

用AUTOIT来管理升级分发公司设计图框及字体库笔者所在的公司隶属制造业&#xff0c;想必大家第一反映就会想到CAD软件,笔者所在的公司是个有"年头"的公司&#xff0c;当然地&#xff0c;从CadR14,2000,2004,2006......一直到最新的2015&#xff0c;这些如繁星的CAD版…

创建二叉树

第一次尝试 //.h 部分的代码 #pragma once//使用这种方式来重命名数据类型&#xff0c;这样可以很方便的修改后续数据的数据类型&#xff0c;相当于#define的作用 typedef char BTreeType;//创建二叉树结构体 typedef struct BTree {//二叉树保存的数据BTreeType _date;//指向…

kurogo 搭建出现问题整理

2019独角兽企业重金招聘Python工程师标准>>> 英语水平有限啊&#xff0c;溜鸡500还是不够的。 Download the Kurogo-Mobile-Web zip archive. This includes the Kurogo-Mobile-Web core code and an example siteExtract the contents of the archive to a folder …