您的位置:首页 > 其它

算法导论第六章-堆排序

2014-10-26 09:31 337 查看


一、概念


1.定义


(1)堆heap

堆的本质是个数组,但是可以看成一个近似的完全二叉树。


(2)最大堆max-heap

根结点的值大于等于子结点的值,根结点(堆顶)的值始终是最大的。


(3)最小堆min-heap

跟结点的值小于等于子结点的值


(4) height

根结点到叶结点最长简单路径上边的数目。


2.性质

(1)堆可以被视为一棵完全二叉树,二叉树的层次遍历结果与数组元素的顺序对应,树根为A[1]。对于数组中第i个元素,可得其父节点,子结点的下标

PARENT(i)
return i/2
LEFT(i)
return 2i
RIGHT(i)
return 2i+1



二、程序


1.堆的结构

A
:堆数组

length[A]:数组中元素的个数

heap-size[A]:存放在A中的堆的元素个数


2.在堆上的操作

(1)MAX-HEAPIFY(A, i),维护最大堆的性质,时间复杂度为O(lgn)。

(2)BUILD-MAX-HEAP(A),从无序数组中构造最大堆,时间复杂度为O(n)。

(3)HEAPSORT(A),堆排序,时间复杂度为O(nlgn)。


3.堆的应用

利用堆实现一个最大优先级队列,最大优先队列的实质就是无论进行什么操作,始终维护最大堆的性质,堆顶始终保持最大,优先级最高

(1)HEAP-MAXIMUM(A),返回堆中的最大元素,时间复杂度为O(1)。

(2)HEAP-INCREASE-KEY(A, i, key),用key代替下标为i的元素,O(lgn)。

(3)HEAP-EXTRACT-KEY(A),去掉堆顶并返回,还要维护堆的性质,O(lgn)。
(4)MAX-HEAP-INSERT(A, key),插入元素,并维护堆的性质,O(lgn)。

(5)  HEAP-DELETE(A,i),删除下标为i的元素,并维护堆的性质,O(lgn)。

代码:

//头文件
#include <iostream>
#include <stdio.h>
using namespace std;

//宏定义
#define N 1000
#define PARENT(i) (i)>>1<span style="white-space:pre">	</span>
#define LEFT(i) (i)<<1
#define RIGHT(i) ((i)<<1)+1

class Heap
{
public:
//成员变量
int A[N+1];
int length;
int heap_size;
//构造与析构
Heap(){}
Heap(int size):length(size),heap_size(size){}
~Heap(){}
//功能函数
void Max_Heapify(int i);
void Build_Max_Heap();
void HeapSort();
//优先队列函数
void Heap_Increase_Key(int i, int key);
void Max_Heap_Insert(int key);
int Heap_Maximum();
int Heap_Extract_Max();
void Heap_Delete(int i);
//辅助函数
void Print();
};

//使以i结点为根结点的子树成为最大堆,调用条件是确定i的左右子树已经是堆,时间是O(lgn)
//递归方法
void Heap::Max_Heapify(int i)
{
Print();
int l = LEFT(i), r = RIGHT(i), largest;
//选择i、i的左、i的右三个结点中值最大的结点
if(l <= heap_size && A[l] > A[i])
largest = l;
else largest = i;
if(r <= heap_size && A[r] > A[largest])
largest = r;
//如果根最大,已经满足堆的条件,函数停止
//否则
if(largest != i)
{
//根与值最大的结点交互
swap(A[i], A[largest]);
//交换可能破坏子树的堆,重新调整子树
Max_Heapify(largest);
}
}

//建堆,时间是O(nlgn)
void Heap::Build_Max_Heap()
{
heap_size = length;
//从堆中中间元素开始,依次调整每个结点,使其符合堆的性质,因为堆的后半段数组都是树的叶子结点,每个叶结点可以看成只包含一个元素的堆,因此可以从length/2开始调用<span style="font-family: Arial;">Max_Heapify去建立一个堆</span>
for(int i = length / 2; i >= 1; i--)
Max_Heapify(i);
}

//堆排序,从小到大,每次把堆顶(最大)与最后一个元素交换,这个最大的值即确定,然后对前面的堆维护最大堆的性质,循环即可。时间是O(nlgn)
void Heap::HeapSort()
{
//使无序数组成为最大堆
Build_Max_Heap();
for(int i = length; i > 1; i--)
{
//将最大的元素即堆顶放到最后
swap(A[1], A[i]);
//堆的大小减一,然后再对前heap_size-1个元素维护堆的性质
heap_size--;
//堆顶由于刚才的swap破坏了性质,调用<span style="font-family: Arial;">Max_Heapify使其重新变成最大堆</span>
Max_Heapify(1);
}
}

//将元素i的关键字增加到key,要求key>=A[i]
void Heap::Heap_Increase_Key(int i, int key)
{
if(key < A[i])
{
cout<<"new key is smaller than current key"<<endl;
exit(0);
}
A[i] = key;
//跟父比较,若A[PARENT(i)]<A[i],则交换
//若运行到某个结点时A[PARENT(i)]>A[i],就跳出循环
while(A[PARENT(i)] > 0 && A[PARENT(i)] < A[i])
{
swap(A[PARENT(i)], A[i]);
i = PARENT(i);
}
}

//把key插入到集合A中
void Heap::Max_Heap_Insert(int key)
{
if(heap_size == N)
{
cout<<"heap is full"<<endl;
exit(0);
}
heap_size++;length++;
A[heap_size] = -0x7fffffff;
Heap_Increase_Key(heap_size, key);
}
//返回A中最大关键字,时间O(1)
int Heap::Heap_Maximum()
{
return A[1];
}
//去掉并返回A中最大关键字,时间O(lgn)
int Heap::Heap_Extract_Max()
{
if(heap_size < 1)
{
cout<<"heap underflow"<<endl;
exit(0);
}
//取出最大值
int max = A[1];
//将最后一个元素补到最大值的位置
A[1] = A[heap_size];
heap_size--;
//重新调整根结点
Max_Heapify(1);
//返回最大值
return max;
}
//删除堆中第i个元素
void Heap::Heap_Delete(int i)
{
if(i > heap_size)
{
cout<<"there's no node i"<<endl;
exit(0);
}
//把最后一个元素补到第i个元素的位置
int key = A[heap_size];
heap_size--;
//如果新值比原A[i]大,则向上调整
if(key > A[i])
Heap_Increase_Key(i, key);
else//否则,向下调整
{
A[i] = key;
Max_Heapify(i);
}
}

void Heap::Print()
{
int i;
for(i = 1; i <= length; i++)
{
if(i > 1)cout<<',';
else cout<<"==> A = {";
cout<<A[i];
}
cout<<'}'<<endl;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法导论 堆排序