数据结构笔记(七)——二叉堆(Binary heap)(1)

这一章讲的是优先队列,实现优先队列有各种方式,二叉堆是比较常见的。

系统管理中有的程序优先级比较高,有时我们要调整某个程序的优先级或者有时需要结束某个进程,这种操作无法借助队列(先进先出)完成。我们提出了优先队列(priority queue)。优先队列是至少允许insert(插入,类似于队列中的入队)和deleteMin(删除最小者,等价于队列中的出队,功能是找到、返回、删除队列中的最小元素)这两种操作的数据结构。这么说满足这两种操作的就可以称为优先队列了。

那么我们要考虑的是实现起来的效率。简单的实现方式,我们可以用链表实现,第一种是以O(1)插入,那么deleteMin的代价是O(N),我们需要遍历链表。第二种是让表保持排序的状态,这样子deleteMin的代价是O(1),插入的代价是O(N)。考虑到deleteMin的次数不多于insert,前者似乎要好一点。还可以用二叉查找树,这两种操作的代价是O(logN)。但二叉查找树还支持其他很多操作,将它用于优先队列就是杀鸡用牛刀了。不如把表和树结合起来吧,用表的形式存储数据,用树的方式操作,这就是二叉堆了,存储数据用的是数组,操作时把它当成树。可是这怎么能做到呢?我们借助了完全二叉树,就是完全被填满的二叉树,底层的元素从左到右。

完全二叉树可以用在优先队列是因为它很有规律:对于数组中位置为i的元素,其左儿子在2i处,右儿子在2i+1处,其父亲在i/2处。另外,我们可以证明,高为h的二叉树有2^h ~ 2^(h+1)-1个节点,也就是说完全二叉树的高为logN,这为执行的效率提供了保障。

如果我们想要快速的执行deleteMin,那么最好让最小元素在根节点。根据递归的思想,把每个子树看成一个二叉堆,那么我们需要保证任意节点都会小于他的所有孩子。上面两点就是二叉堆的结构性和堆序性,我们在操作时要保持这两种性质。

对于插入操作,插入一个新元素时,会破坏堆序性,我们采用上滤(percolate up)维持这种性质。具体一点就是在最后的位置留一个空位(空穴),比较这个空位的父节点元素和要插入的元素的大小,如果父节点元素较大,则将父节点元素放入空穴,此时父节点的位置成了空穴。一直向上直到要插入的元素不小于父节点元素,则将插入元素放入空穴中。(详见代码)。

删除最小元的操作,最小元在根节点处,我们删掉了这个节点的元素,根节点就是个空穴了,我们要采用下滤的方法把这个空穴填满,不能破坏结构性。和上滤差不多,比较节点的两个孩子,把较小的孩子放在这个节点处,那么孩子节点就空出来了,一直向下。要注意的是,我们要使得最后树的底层元素是从左到右填充的,所以删除一个元素后,表现在树的结构上就是最后一个元素没了,我们要把最后一个元素放在某个合适的位置,下面还是给一个例子吧

    

                             原                                                                下滤一次

 

  

                      下滤两次                                                 最后一个元素放到空穴中

再来一个例子

 

                                 原                                                               过滤一次

                             最后一个元素放入空穴                                             结果

另外有一些操作,比如降低/增加关键字的值,就可以使系统管理程序中的某个程序改变优先级,删除节点可以通过降低关键字优先级再deleteMin,当进程被用户中止时,就需要从队列中删除了。

稍微提一下d-堆,d-堆是二叉堆的简单推广,不同之处在于d-堆的所有节点都有d个儿子,如此看来,二叉堆是d-堆的一种,为2-堆。d-堆比二叉堆要浅,insert的代价为O(log{_{d}}N),deleteMin由于下滤时需要比较所有儿子的大小,每次花费d-1次比较,时间代价为O(dlog{_{d}}N),当d为常数时,时间还是O(logN),当d很大时,如d=N/2,那时间就不再是O(logN)了。一般会选择d为2的幂,可以通过移位进行除法从而找到父节点的位置,否则就要进行乘除,大大增加了运行时间。

代码实现二叉堆:

BinHeap.h

#pragma once
#include<iostream>
using namespace std;

#define MINDATA -32767
#define MINSIZE 10
struct HeapStruct;
typedef int ElementType;
typedef HeapStruct *BinHeap;
BinHeap initialize(unsigned int maxSize);
void makeEmpty(BinHeap h);
void destroy(BinHeap h);
void insert(BinHeap h, ElementType e);
ElementType findMin(BinHeap h);
ElementType deleteMin(BinHeap h);
bool isFull(BinHeap h);
bool isEmpty(BinHeap h);
void percolateDown(BinHeap h, unsigned int i);
void buildHeap(BinHeap h);
void printHeap(BinHeap h);
struct HeapStruct
{
	ElementType *data;
	unsigned int capacity;
	unsigned int heap_size;
};

BinHeap.cpp

#include "stdafx.h"
#include "BinHeap.h"
BinHeap initialize(unsigned int max_size)
{
	if (max_size<MINSIZE)
	{
		cerr << "heap size is too small" << endl;
		return nullptr;
	}
	BinHeap heap = (BinHeap)malloc(sizeof(HeapStruct));
	if (heap==nullptr)
	{
		cerr << "out of space" << endl;
		return nullptr;
	}
	heap->data = (ElementType*)malloc(sizeof(ElementType)*(max_size + 1));
	if (!heap->data)
	{
		free(heap);
		cerr << "out of space" << endl;
		return nullptr;
	}
	heap->capacity = max_size;
	heap->heap_size = 0;
	heap->data[0] = MINDATA;
	return heap;
}
void makeEmpty(BinHeap h)
{
	if (h)
	{
		h->heap_size = 0;
	}
}
void destroy(BinHeap h)
{
	if (h)
	{
		if (h->data)
		{
			free(h->data);
		}
		free(h);
	}
}
void insert(BinHeap h, ElementType e)
{
	if (isFull(h))
	{
		return;
	}
	unsigned int i;
	for (i=h->heap_size+1;h->data[i/2]>e;i/=2)//上滤
	{
		h->data[i] = h->data[i / 2];
	}
	h->data[i] = e;
}
ElementType findMin(BinHeap h)
{
	if (isEmpty(h))
	{
		cerr << "empty heap" << endl;
		return MINDATA;
	}
	return h->data[1];
}
ElementType deleteMin(BinHeap h)//下滤,最后一个元素放置的位置
{
	unsigned int i, child;
	if (isEmpty(h))
	{
		cerr << "empty heap" << endl;
		return MINDATA;
	}
	ElementType min_ele = h->data[1];
	ElementType last_ele = h->data[h->heap_size--];//取得最后一个元素并将size-1
	for (i =1;i*2 <=h->heap_size;i=child)
	{
		child = 2 * i;
		if (child!=h->heap_size&&h->data[child]>h->data[child +1])//child!=h->heap_size
		{
			child++;
		}
		if (last_ele>h->data[child])//让最后一个元素插入到合适的位置
		{
			h->data[i] = h->data[child];
		}
		else
		{
			break;
		}
		
	}
	h->data[i] = last_ele;
	return min_ele;
}
bool isFull(BinHeap h)
{
	return h->capacity == h->heap_size;
}
bool isEmpty(BinHeap h)
{
	return h->heap_size == 0;
}
void percolateDown(BinHeap h, unsigned int i)
{
	unsigned left, right,min_index;
	left = i << 1;
	right = left + 1;
	min_index = i;
	if (left<=h->heap_size&&h->data[left]<h->data[min_index])
	{
		min_index = left;
	}
	if (right <= h->heap_size&&h->data[right] < h->data[min_index])
	{
		min_index = right;
	}
	if (min_index!=i)
	{
		swap(h->data[min_index], h->data[i]);
		percolateDown(h, min_index);
	}
	
}
void buildHeap(BinHeap h)
{
	ElementType input;
	unsigned int i = 1;
	cout << "input: (less tahn " << h->capacity << ")" << endl;
	while (i <= h->capacity)
	{
		cin >> input;
		if (input==-1)
		{
			break;
		}
		h->data[i] = input;
		h->heap_size++;
		++i;
	}
	for (unsigned int i=h->heap_size/2;i>0;--i)
	{
		percolateDown(h, i);
	}
}
void printHeap(BinHeap h)
{
	for (int i=1;i<=h->heap_size;++i)
	{
		cout << h->data[i] << " ";
	}
	cout << endl;
}

test.cpp

#include "stdafx.h"
#include "BinHeap.h"

int main()
{
	BinHeap heap = initialize(30);
	buildHeap(heap);
	printHeap(heap);
	deleteMin(heap);
	printHeap(heap);
	destory(heap);
	return 0;
}

   

结果

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值