堆和哈夫曼树

  • 什么是堆
    • 优先队列(Priority Queue)
    • 堆的抽象数据类型描述
      • 1.最大堆的创建
      • 2. 最大堆的插入
      • 3. 最大堆的删除
      • 最大堆的建立
  • 哈夫曼树与哈夫曼编码
    • 哈夫曼树的定义
    • 哈夫曼树的构造
    • 哈夫曼树的特点
  • 哈夫曼编码

什么是堆

优先队列(Priority Queue)

特殊的“队列”,取出元素的顺序依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序

  1. 采用数组实现优先队列
  • 插入——元素总是插入尾部
  • 删除——查找最大(或最小)关键字,从数组中删去需要移动的元素
  1. 采用链表实现优先队列
  • 插入——元素总是插入链表头部
  • 删除——查找最大(或最小)关键字删除结点
  1. 采用有序数组实现优先队列
  • 插入——找到合适位置移动元素并插入
  • 删除——删去最后一个元素
  1. 采用有序链表实现优先队列
  • 插入——找到合适的位置插入元素
  • 删除——删除首元素或最后元素
  1. 采用完全二叉树存储结构(最优)

堆的两个特性

  1. 结构性:数组表示的完全二叉树
  2. 有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)
  • 最大堆(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
}

最大堆的建立

堆的应用:堆排序

  1. 通过插入操作,将N个元素一个个相继插入到一个初始为空的堆中去
  2. 线性时间复杂度下建立最大堆
  • 将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
  • 字符只在叶结点

参考题目05-树9:Huffman Codes (30分)

你可能感兴趣的:(数据结构:树和二叉树)