堆
什么是堆
优先队列(Priority Queue)
特殊的“队列”,取出元素的顺序依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序
- 采用数组实现优先队列
- 插入——元素总是插入尾部
- 删除——查找最大(或最小)关键字,从数组中删去需要移动的元素
- 采用链表实现优先队列
- 插入——元素总是插入链表头部
- 删除——查找最大(或最小)关键字删除结点
- 采用有序数组实现优先队列
- 插入——找到合适位置移动元素并插入
- 删除——删去最后一个元素
- 采用有序链表实现优先队列
- 插入——找到合适的位置插入元素
- 删除——删除首元素或最后元素
- 采用完全二叉树存储结构(最优)
堆的两个特性
- 结构性:数组表示的完全二叉树
- 有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)
- “最大堆(MaxHeap)”,也称“大顶堆”:最大值
- “最小堆(MinHeap)”,也称“小顶堆”:最小值
堆的抽象数据类型描述
类型名:最大堆(MaxHeap)
数据对象集:完全二叉树,每个结点的元素值不小于其子节点的值
1.最大堆的创建
typedef struct HeapStruct *MaxHeap;
struct HeapStruct
{
ElementType *Elements;/*存储堆元素的数组*/
int Size;/*堆的当前元素个数*/
int Capacity;/*堆的最大容量*/
}
MaxHeap Create(int MaxSize)
{
MaxHeap H = malloc(sizeof(struct HeapStruct));
H->Elements = malloc((MaxSize+1) * sizeof(ElementType));
H->Size = 0;
H->Capacoty = MaxSize;
H->Elements[0] = MaxData;/*定义为“哨兵”,为大于堆中所有元素的值,方便以后操作*/
return H;
}
2. 最大堆的插入
void Insert(MaxHeap H,ElementType item)
{
int i;
if(isFull(H))
{/*最大堆已满*/
return;
}
i = ++H->Size;/*i指向插入后堆中的最后一个元素的位置*/
for( ;H->Elements[i / 2] < item; i /= 2)
{
H->Elements[i] = H->Elements[i / 2];
}
H->Elements[i] = item;
}
3. 最大堆的删除
ElementType DeleteMax(MaxHeap H)
{
int parent,child;
ElementType MaxItem,temp;
if(IsEmpty(H))
{/*如果堆为空就没有必要删除*/
return;
}
MaxItem = H->Elements[1];/*取出根结点最大值*/
temp = H->ElementType[H->Size--];/*删除一个元素后大小要减1*/
for(parent = 1; parent * 2 <= H->Size/*判别有没有左儿子*/; parent = child)
{
child = parent * 2;
if( (child != H->Size) /*判别有没有右儿子*/&&
(H->Elements[child] < H->Elements[child + 1]) )
{/*把child指向左右儿子中大的*/
child++;
}
if(temp >= H->Elements[child]) break;/*如果temp大于左右儿子就直接插入*/
else
{
H->ELements[parent] = H->Elements[child];
}
}
H->Elements[parent] = temp;
return MaxIteml
}
最大堆的建立
堆的应用:堆排序
- 通过插入操作,将N个元素一个个相继插入到一个初始为空的堆中去
- 在线性时间复杂度下建立最大堆:
- 将N个元素按输入顺序存入,先满足完全二叉树的结构特性
- 调整各结点的位置以满足最大堆的特性
哈夫曼树与哈夫曼编码
哈夫曼树的定义
带权路径长度(WPL),设二叉树有n个叶子结点,每个叶子结点带有权值Wk,从根节点到每个叶子结点的长度为Ik,则每个叶子结点的带权路径长度之和就是:
最优二叉树和或哈夫曼树:WPL最小的二叉树
哈夫曼树的构造
每次把权值最小的两棵二叉树合并
typedef struct TreeNode *HufffmanTree;
struct TreeNode
{
int weight;
HuffmanTree Left,Right;
}
HuffmanTree Huffman( MinHeap H )
{
int i;
HuffmanTree T;
BuildMinHeap(H);/*将H->Elements[]按权值调整为最小堆*/
for(int i = 1; i < H->Size; i++)
{
T = malloc(sizeof(struct TreeNode));
T->Left = DeleteMin(H);/*从最小堆中删除一个结点*/
T->Right = DeleteMin(H);/*从最小堆中删除一个结点*/
T->Weight = T->Left->Weight + T->Right->Weight;
Insert(H,T);
}
T = DeleteMin(H);
return T;
}
哈夫曼树的特点
- 没有度为1的结点
- n个叶子结点的哈夫曼树共有2n - 1个结点
- 哈夫曼树任意非叶结点的左右子树交换后仍是哈夫曼树
- 对于同一组权值,存在不同构的两棵哈夫曼树,但WPL相同
哈夫曼编码
前缀码(prefix code):任何字符的编码都不是另一个字符编码的前缀,可以避免二义性
采用二叉树进行编码
- 左右分支:0,1
- 字符只在叶结点上