数据结构 --- c语言堆的实现

本文介绍了堆的基本概念,如数组描述、大顶堆与小顶堆的区别,以及堆的插入、调整过程。通过结构体和示例代码详细展示了如何创建堆、插入元素并维护堆的有序性。堆排序作为应用,演示了如何从堆中有序地取出最大元素。
摘要由CSDN通过智能技术生成

 什么是堆?

  • 一段连续的内存,一般用数组描述堆,用二叉树的方式分析

  • 堆中的数据一般是无序的

  • 用数组描述堆,一般情况下,第一个空间(下标为 [0] 的空间)不使用,如果使用了第一段内存就会不满足序号的特性

  • 上层节点(父节点)比下层节点(子节点)大--->大顶堆

  • 上层节点(父节点)比下层节点(子节点)小--->小顶堆

  • 大顶堆 || 小顶堆在数组中是无序的,按照二叉树的排布:父节点的值大于子节点的值(大顶堆)

  • 完全二叉树:中间不能有空,满足数组的连续性的特点

  • 堆的插入和删除根据序号的特性去实现:从第一个顶点开始编序号,依次能够对应数组中的序号,满足序号的需求,从1开始编序号,子节点的序号/2 == 父节点的序号

  • 用二叉树的方式去分析,实质上还是一段内存,还是一个数组

 堆的插入(向上渗透)

  • 插入的元素通常放在当前堆的最后面:插入1,把1放在当前堆的最后面,把 1 和它父节点的值相比较,如果满足大顶堆的需求:当前节点小于父节点的值,就不需要调整;

  • 插入100,把100放在当前堆的最后面,100和35做比较,不满足大顶堆的需求,元素向上渗透,把当前节点的值和父节点的值相比较(需要知道父节点的序号:curSize/2),100大于35,大于78,大于89,一直向上渗透,交换位置;

  • 不是按照相邻的去做比较,而是按照二叉树的特性去做比较

 堆的结构体描述

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
typedef struct 
{
	int curSize;     //记录当前元素个数
	int* mem;        //堆的内存
	int maxSize;
}HEAP,*LPHEAP;

 创建堆--->数组实现 用结构体指针表示堆

LPHEAP createHeap(int maxSize)
{
	LPHEAP heap = (LPHEAP)malloc(sizeof(HEAP));            //动态内存申请
	assert(heap);
    //数据初始化
	heap->curSize = 0;
	heap->maxSize = maxSize;
	heap->mem = (int*)malloc(sizeof(int) * heap->maxSize); //堆存的数据 二次申请内存
	assert(heap->mem);
	return heap;
}

 万金油函数 

//大小
int size(LPHEAP heap) 
{
	return heap->curSize;
}
//判断堆是否为空
int empty(LPHEAP heap) 
{
	return heap->curSize == 0;
}

 调整堆 

  • 调整堆把元素移到正确的位置

  • 要插入的元素在当前位置 curPos

  • 把当前位置(子节点 curPos) 与 当前位置/2(父节点 curPos/2) 的值做比较:比较到 父节点的值 >当前节点的值 就结束;如果 父节点的值 < 当前节点的值,比较到第一个位置就结束;当前节点的值成为树的根节点为止;如果 父节点的值 == 当前节点的值,没必要向上渗透

//要调整的堆 当前元素的下标
void move(LPHEAP heap, int curPos) 
{
	while (curPos > 1)                  //>1 一直往上冒
	{
		int max = heap->mem[curPos];    //假设当前位置是最大的
		//子节点/2=父节点
		int parentPos = curPos / 2;     //求出父节点的下标
		if (max > heap->mem[parentPos]) //比较两个元素 当前元素>父节点中的元素就向上渗透
		{
			heap->mem[curPos] = heap->mem[parentPos];
			heap->mem[parentPos] = max; //交换父节点和子节点的值
			curPos = parentPos;         //下标向1靠近
		}
		else                            //小于的情况说明放在了合适的位置 不用往上冒
		{
			break;
		}
	}
}

 堆的插入

//要插入的堆 要插入的数据
void insertHeap(LPHEAP heap, int data) 
{
	if (heap->curSize == heap->maxSize-1) //放进去要考虑满的状态 第1个位置不存数据需要减1
	{
		return;
	}
	//存数据:直接放在当前数组后面即可 前置++第一个位置不存数据 存第1个元素放在[1]下标中
	heap->mem[++heap->curSize] = data;   //heap->mem[0] 第1个位置永远都用不到 
	//满足大顶堆的要求: 父节点的值大于子节点的值
	//向上渗透 调整堆
	move(heap, heap->curSize);
}

 有序性出堆--->堆排序 

  • 一般情况下从第1个元素开始出堆:在下一层找一个最大的(注意要找最大的),和第一个元素交换位置,第1个元素向下渗透,把第一个元素(要出堆的元素)放在数组的最后面,curSize--,相当于把元素删除了--->数组的伪删除

  • 每次出的都是最大的元素

  • 返回要出堆的元素

  • 向下渗透注意要把元素和最后一个位置的元素交换

特殊情况 

//要出的堆
int  popHeap(LPHEAP heap) 
{
	int max = heap->mem[1];			        //第一个元素肯定是最大的 下标为[1]的元素
	int curPos = 1;                         //当前下标
	int childPos = curPos * 2;		        //子节点下标--->每一组中的第一个元素
	while (childPos <= heap->curSize) 
	{
		int temp = heap->mem[childPos];     //子节点的值
		//横向比较找最大值 只要比较横向的2个值 childPos + 1为右边的值
		if (childPos + 1 <= heap->curSize && temp < heap->mem[childPos + 1]) 
		{
			temp = heap->mem[++childPos];	//如果左边的值<右边的值 需要往右走
		}
		heap->mem[curPos] = temp;           //如果往左边走 接着往下找即可
		curPos = childPos;                  //当前pos往下走
		childPos *= 2;
	}
	heap->mem[curPos] = heap->mem[heap->curSize]; //找到最终交换的元素
	move(heap, curPos);                     //存在不满足规则的情况做调整
	heap->curSize--;
	return max;
}

 测试代码

int main()
{
	srand((unsigned int)time(NULL));     //设置随机函数种子
	LPHEAP heap = createHeap(11);        //创建堆
	for (int i = 0; i < 11; i++) 
	{
		insertHeap(heap, rand() % 100);
	}
	printf("堆中数据:\n");
	for (int i = 1; i < 11; i++) 
	{
		printf("%d\t",heap->mem[i]);   
	}
	printf("\n");
	printf("堆排序:\n");
	while (!empty(heap)) 
	{
		printf("%d\t", popHeap(heap));
	}
	printf("\n");
	return 0;
}
//测试
堆中数据:
76    76    59    48    11    14    56    43    47    2
堆排序:
76    76    59    56    48    47    14    43    11    2
76    76    59    56    48    47    43    14    11    2    //调整堆
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

qiuqiuyaq

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

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

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

打赏作者

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

抵扣说明:

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

余额充值