数据结构自学笔记(C语言)优先队列(堆)

队列我们之前学过,FIFO先进先出,这种做法在一些场合没问题,但在有些特殊的场合就不那么适用了。比如打印机工作,就是个队列,但是有时候会有一些额外的要求。比如医院排队,然后来了个领导,虽然他是后来的,但是我想把它放到第一个,这个用队列就实现不了,需要引入一个新的概念:优先队列。
优先队列:优先队列是一种特殊的队列,是在队列的基础上引入优先级的概念。优先队列是允许至少下列两种操作的数据结构:Insert、DeleteMin。Insert等价于入队操作,DeleteMin等价于出队操作,但是DeleteMin是出队优先级最高的数据元素。为了实现优先队列的想法,我么使用二叉堆。
:堆有两个性质,结构性和堆序性。结构性就是和完全二叉树一样,依次插入,先左孩子后右孩子,兄弟的孩子都满了在去下一层。这样比较好,通过数组的序号就能找到双亲和儿子,i的左儿子在2i,右儿子在2i+1,父亲在i/2取整上,不用用链表来确定关系了。堆序性就是数据元素的关键字比他的左右孩子的关键字都小(或者大,本节都以小讲了)。这样就能保证关键字最小的永远在堆顶,这样每次出队出的就是关键字最小的元素了。
因此一个堆数据结构将由一个数组(不管关键字是什么类型)、堆最大容量以及当前堆大小组成。准备写程序,堆只要写初始化、插入、删除最小三个程序就好了。开始思考:
1.数组都是从0开始的,但是加上0就不满足2i和2i加一的性质了,所以0那个点应该不使用。也就是说假如数组中有5个元素,那就存在arr[1]-arr[5],实际上存了六个元素,但是arr[0]是个费元素,所以我们申请空间的时候要申请maxsize+1个int的大小。
2.堆的插入程序该怎么写,首先应该把他放在数组最后,和他的双亲比较如果比双亲小,就调换位置,不小就结束。调换完双亲在和他的双亲比,一直比。其实就是i一直除以2取整的过程,最后截止条件是什么呢,要么到根也就arr[1]上,要不提前跳出循环。但是i=1的时候就不用比了吗?还要比,和arr[0]比,这个时候不管比的结果如何都不能换了,因为arr[0]是个没有用的数据元素啊,所以arr[1]不能比arr[0]小,所以我们让arr[0]里面放一个最小的值(如果是大顶堆,那就放一个最大的元素)
3.删除应该是什么想法呢?取很好取,只要把arr[1]取走就可以了,因为堆的特性就是arr[1]最小,所以删除难的是删完后堆的复原。思想如下:删完之后从他的两个孩子里挑一个小的提上来,然后在把他孩子的位置跟他孩子的孩子比(不能比到最后一个叶子,因为最后一个叶子要找地方插入)。一直比之后在叶子部分肯定会有一个空地方,再把原先最后的那个叶子放过去(因为满二叉树前面必须是满的),最后再把size-1就好了。结合图说一下,取的时候把1取走了,然后从两个孩子中找一个取代他的。假设是2,2到他的位置,在从2中找一个孩子取代2的位置,但是由于3是最后一片叶子,所以不能动,只能把另一个孩子放过去,然后再把最后一个叶子3放到空缺的位置上,也就是他的兄弟。有人可能会想,直接把3放到2的位置也可以啊,没问题的。逻辑上是没问题,但你写程序的时候就会发现要多加好几个if,会让程序变复杂,像我说的这样写就把所有情况都变成一种情况了,也就是把最后一个去补位置的情况。
在这里插入图片描述
PS:程序用语言描述实在是太别扭了,还是写程序吧,自己出问题了再回头看我写的可能才会理解,上程序吧

#include<stdio.h>//printf函数
#include<stdlib.h>//malloc函数
#define MINDATA 0;
typedef struct Heap
{
	int size;
	int *arr;
	int maxSize;
}*Heap1;

Heap1 InitHeap(int size)
{
	Heap1 h = (Heap1)malloc(sizeof(struct Heap));
	h->arr = (int*)malloc((size+1)*sizeof(int));
	h->size = 0;
	h->maxSize = size;
	h->arr[0]= MINDATA;
	return h;
}

void InsertHeap(int elem,Heap1 h)
{
	int i;
	for(i=++h->size;h->arr[i/2]>elem;i/=2)
	{
		h->arr[i] = h->arr[i/2];
	}
	h->arr[i] = elem;
}

int Delete(Heap1 h)
{
	int elem,elem1;
	int i;
	if( !h->arr[1])
		return -1;
	else
	{
		elem = h->arr[1];
		elem1 = h->arr[h->size];
		h->arr[h->size] = 0;
		for(i=1;2*i<= h->size-1;)
		{
			if(h->arr[2*i]< h ->arr[2*i+1] || 2*i+1 == h->size)
			{
				h->arr[i] = h->arr[2*i];
				i = 2*i;
			}
			else
			{
				h->arr[i] = h->arr[2*i+1];
				i = 2*i+1;
			}
		}
		h->arr[i]=elem1;
		h->size -=1;
	}
	return elem;
		

}

void main()
{
	int i=0;
	int elem;
	Heap1 h1 = InitHeap(15);
	InsertHeap(13,h1);
	InsertHeap(14,h1);
	InsertHeap(15,h1);
	InsertHeap(12,h1);
	InsertHeap(11,h1);
	elem = Delete(h1);
	for(i=1;i<=h1->size;i++)
		printf("%d\n",h1->arr[i]);
}

昨天晚上睡前突然发现程序好像有个问题,有种特殊情况我没有考虑到。就是虽然把最后一个数据元素插到一个叶子上,但有可能会像下图一样,虽然堆可以保证孩子比双亲大,但新插的叶子还是不能保证的。所以在执行完h->arr[i]=elem1;之后还要再加个验证
在这里插入图片描述

h->arr[i]=elem1;
//更正
while(h->arr[i] < h->arr[i/2])
{
	elem1 = h->arr[i];
	h->arr[i] = h->arr[i/2];
	h->arr[i/2] = elem1;
	i/=2;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值