数据结构基础内容(第七篇:堆、哈夫曼树)
# 堆 Heap
优先队列(Priority Queue)
结构性:用 *数组* 表示的完全二叉树;
有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)
* “最大堆(MaxHeap)”,也称“大顶堆”:最大值
* “最小堆(MinHeap)”,也称“小顶堆” :最小值
主要操作有:
• MaxHeap Create( int MaxSize ):创建一个空的最大堆。
• Boolean IsFull( MaxHeap H ):判断最大堆H是否已满。
• Insert( MaxHeap H, ElementType item ):将元素item插入最大堆H。
• Boolean IsEmpty( MaxHeap H ):判断最大堆H是否为空。
• ElementType DeleteMax( MaxHeap H ):返回H中最大元素(高优先级)。
## 结构
```c
typedef struct HNode *MaxHeap;
struct HNode {
ElementType *Elements; // 存储堆元素的数组
int Size; // 堆的当前元素的个数
int Capacity // 堆的最大容量
}
```
## 创建
```c
MaxHeap CreateHeap(int Maxsize)
{
MaxHeap H = (MaxHeap)malloc(sizeof(struct HNode));
H->Elements = malloc((MaxSize+1) * sizeof(ElementType)); // 因为这里是一个数组,需要分配空间
H->Size = 0; // 当前是空的
H->Capacity = MaxSize; // 堆的最大容量
H->Elements[0] = MaxSize; /* 定义“哨兵”为大于堆中所有可能元素的值,便于以后操作 */
return H;
}
```
## 插入
将新增结点插入到从其父结点到根结点的有序序列中
将新item放在最后的位置Size+1, 然后跟他的父节点i/2比较,不断把父节点往下(子节点)移动,直到其父节点大于item
```c
void InsertHeap(MaxHeap H, ElementType item)
{
int i;
if (IsFull(H)){
printf("FULL\n");
return;
}
i = ++H->Size; // H->Size++; i = H->Size; 把新增元素放在末尾H->Size++的位置上
for(; H->Elements[i/2] < item; i/=2){ // 其父节点小于它
H->Elements[i] = H->Elements[i/2]; // 把它的父节点,向下过滤, 插入的item向上过滤
}
// 当它的父结点[i/2]比它大的时候, 跳出循环
H->Elements[i] = item; // 填上item
}
```
# 删除
取出根结点(最大值)元素,同时删除堆的一个结点。
* 最后的元素替补根的位置
* 有序化, 父结点和更大的那个子结点比较,将子结点不断往上移, 直到父结点不子结点大
```c
ElementType DeleteMax(MaxHeap H)
{
int parent, child;
ElementType MaxItem, temp;
if(IsEmpty(H)){
printf("Empty\n");
return;
}
MaxItem = H->Elements[1]; // 取出最大值
/* 用最大堆中最后一个元素从根节点开始向上过滤下层节点 */
temp = H->Elements[Size]; // 把堆中最后一个值,交给temp
Size--;
for(parent=1; parent*2 <= H->Size; parent=child)
{
child = parent*2 // 左儿子
/* child=左右儿子中大的那个, 当右儿子存在,且右儿子的值比左儿子大时,让child=右儿子 */
if((child!= H->Size) &&
(H->Elements[child] < H->Elements[child+1]))
child++;
/* 当temp比child的值大时跳出循环 */
if (temp >= Elements[child])
break;
else
H->Elements[parent] = H->Elements[child]; //当parent < child,这个parent位置上变为child的值
}
H->Elements[parent] = temp;
return MaxItem;
}
```
# 堆排序
方法1:通过插入操作,将N个元素一个个相继插入到一个初 始为空的堆中去,其时间代价最大为O(N logN)。
方法2:在线性时间复杂度下建立最大堆。O(N)
(1)将N个元素按输入顺序存入,先满足完全二叉树的结构特性
(2)调整各结点位置,以满足最大堆的有序特性。
```c
void BuildHeap(MaxHeap H){
int i;
for(i = H->Size/2; i>0; i--)
{// 从最后一个结点的父节点开始,到根节点为止
PercDown(H, i);
}
}
// 下滤函数, 将Maxheap中以H->Data[p]为根的子堆调整为最大堆
void PercDown( MaxHeap H, int p)
{
int parent, child;
X = H->Data[p]; // 取出根节点的值
for(parent = p; parent*2 <= H->Size; parent = child)
{
child = parent * 2;
if( (child != H->Size) && (H->Data[child] < H->Data[child+1]))
{
child++;
}
if (X > H->Data[child])
break;
else // 将X下滤
H->Data[parent] = H->Data[child];
}
H->Data(parent) = X;
}
```
# 哈夫曼树与哈夫曼编码
带权路径WPL Weighted Path Length)长度最小, 最优二叉树.
数据结构:
```c
typedef struct TreeNode *HuffmanTree;
struct TreeNode{
int Weight;
HaffmanTree left;
HaffmantREE Right;
}
```
利用最小堆进行构造:
```c
HuffmanTree Huffman(MinHeap H)
{
int i;
HuffmanTree T;
BuildMinHeap(H); // 将H->Elements[]按照权重调整为最小堆
for(i=1; i < H->Size; i++) // 做size-1次合并
{
T = malloc(sizeof(struct TreeNode)); // 建立新结点
/*从最小堆中删除一个结点,作为新T的左子结点*/
T->Left = DeleteMin(H);
T->Right = DeleteMin(H);
/*计算新权值*/
T->Weight = T->Left->Weight+T->Right->Weight;
Insert( H, T ); /*将新T插入最小堆*/
}
T = Deletemin(H); // 最小堆中的最后一个元素就是指向Huffman树根节点的指针
return T;
}
```
哈夫曼树的特点:
1. 没有度为1的结点
2. n个叶子结点的哈夫曼树共有2n-1个结点;
因为: n2= n0 -1,
总结点数 = n0 + n2 = 2n0 - 1
3. 哈夫曼树的任意非叶节点的左右子树交换后仍是哈夫曼树;
4. 对同一组权值{w1 ,w2 , ...... , wn},存在不同构的两
# 集合及运算
双亲表示法: 孩子指向父节点
数据结构
```c
typedef struct
{
ElementType Data;
int Parent; // 其父节点的下标
} SetType;
```
## 查找某个元素所在的集合
用根节点表示
```c
int Find(SetType S[], ElementType X)
{
/* 在数组S中查找值为X的元素所属的集合, MaxSize为数组最大长度 */
int i;
for(i=0; i<MaxSize && S[i].Data != X; i++)
if (i >= MaxSize) // 未找到
return -1;
for(; S[i].Parent >= 0; i = s[i].Parent); // 向上找它的父节点
return i;
}
```
## 集合的并运算
如果它们不同根,则将其中一个根结点的父结点指针设置成
另一个根结点的数组下标。
```c
void Union( SetType S[], ElementType X1, ElementType X2 )
{
int Root1, Root2;
Root1 = Find(S, X1);
Root2 = Find(S, X2);
if( Root1 != Root2 )
S[Root2].Parent = Root1;
}
```
为了使树更矮,合并时按秩合并
直接用一个数组表示,不用之前的数据结构了
```c
#define MAXN 1000 /* 集合最大元素个数 */
typedef int ElementType; /* 默认元素可以用非负整数表示 */
typedef int SetName; /* 默认用根结点的下标作为集合名称 */
typedef ElementType SetType[MAXN]; /* 假设集合元素下标从0开始 */
SetName Find(SetType S, ElementType X){
for(; S[X] > 0; X=S[X]);
return X.
}
void OldUnion(SetType S, SetName Root1, SetName Root2){
S[Root2] = Root1;
}
void Union( SetType S, SetName Root1, SetName Root2 )
{ /* 这里默认Root1和Root2是不同集合的根结点 */
/* 保证小集合并入大集合 */
if ( S[Root2] < S[Root1] ) { /* 如果集合2比较大 */
S[Root2] += S[Root1]; /* 集合1并入集合2 */
S[Root1] = Root2;
}
else { /* 如果集合1比较大 */
S[Root1] += S[Root2]; /* 集合2并入集合1 */
S[Root2] = Root1;
}
}
```
路径压缩
```c
SetName Find( SetType S, ElementType X )
{
if ( S[X] < 0 ) /* 找到集合的根 */
return X;
else
return S[X] = Find( S, S[X] ); /* 路径压缩 */
}
```