数据结构学习之——最大堆、最小堆(优先队列、哈夫曼树)

最大堆(优先队列)

最大堆:根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。

最小堆:根结点的键值是所有堆结点键值中最小者,且每个结点的值都比其孩子的值小。

最大堆(优先队列)的生成

生成最大堆:最大堆通常都是一棵完全二叉树,因此我们使用数组的形式来存储最大堆的值,从1号单元开始存储,因此父结点跟子结点的关系就是两倍的关系。

因此创建一个最大堆只需要在数组位置为1的地方插入第一个元素。

数组从第一个开始存放的原因有两个:

1.从元素1开始存储数据可以保证结点的子树的下标为父亲的2倍
2.在起点放置哨兵在接下来最大堆的插入删除有一定帮助

在这里插入图片描述
该最大堆在数组中的存储顺序为
[Flag]-[100]-[19]-[36]-[17]-[3]-[25]-[1]-[2]-[7]

最大堆(优先队列)的删除

最大堆的操作先从简单的来,最大堆的设计就是为了方便将最大元素或最小元素以最小的代价给出。

从上面的树中,删除一个最大元素将使得根结点被弹出。需要对根结点经行补全,此时要寻找最大堆中剩余节点中的最大元素补到根结点的位置,也就是数组中下标为1的位置。

很显然,下面的元素中,最大元素必然是根节点的左右子树中较大的那一个。
我们将根节点补充为其较大的那一个儿子。而这个被拿去补充根节点的元素所占的位置仍然需要其较大的子结点去补充,这样不断向下,直到被拿去补充的元素不再拥有左右子树,退出循环。

程序:

//最大堆的删除,刚写完没写注释,后续有空会加上
struct tree *Delete(void) {
	struct tree *T;
	T = malloc(sizeof(LEN_TREE));
	int change;
	int i;
	T->data = MaxHeap[1].data;
	T->right = MaxHeap[1].right;
	T->left = MaxHeap[1].left;

	MaxHeap[1].data = MaxHeap[length].data;
	MaxHeap[length].data = 0;
	length--;
	for (i = 1; i < length; i *= 2) {
		if (MaxHeap[2 * i].data>MaxHeap[2 * i + 1].data) {
			if (MaxHeap[i].data < MaxHeap[2 * i].data) {
				change = MaxHeap[i].data;
				MaxHeap[i].data = MaxHeap[2 * i].data;
				MaxHeap[2 * i].data = change;
			}
			else {
				break;
			}
		}
		else {
			if (MaxHeap[i].data < MaxHeap[2 * i+1].data) {
				change = MaxHeap[i].data;
				MaxHeap[i].data = MaxHeap[2 * i+1].data;
				MaxHeap[2 * i+1].data = change;
			}
			else {
				break;
			}
		}
	}
	return T;
}

最大堆(优先队列)的插入

最大的的插入与删除类似于反向操作。
我们首先将要插入的数据放在最大堆的尾部,然后比较其父亲子树与该结点的参数大小,若插入的参数大于其父亲结点的参数,则二者做交换。在不断向上交换的过程中,循环什么时候结束呢?
没错,当比较的元素N/2为0,我们先前定的Flag时,则退出循环,若是最大堆可以将Flag定位无穷大,反之可以定为0或无穷小。

程序:

//最大堆的插入,刚写完没写注释,后续有空会加上
int  Insert(struct tree T) {
	int len;
	length++;
	len = length;

	MaxHeap[length] = T;
	if ((len / 2) == 0) {
		return 0;
	}
	while (len) {
		if (MaxHeap[len].data > MaxHeap[len / 2].data && len / 2!=0) {
			MaxHeap[len].data = MaxHeap[len / 2].data;
			len /= 2;
		}
		else {
			break;
		}
		MaxHeap[len] = T;
	}
	MaxHeap[len] = T;
	return 0;
}

哈夫曼树

给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

以上为哈夫曼树的定义,哈夫曼树的创建其实是一个比较复杂的过程。但是在最大堆的配合下,可以让哈夫曼树的创建变得十分简单。

举个栗子。

很多时候我们需要为一些代号进行编码,例如A,B,C,D,我们需要对其进行编码时,我们可以通过00,01,10,11,这样的方式来进行编码。此时,我们只需要一棵高度为2的二叉树就可以对A,B,C,D进行编码。

在这里插入图片描述
但是当我们在处理数据的同时,数据之间存在权重的关系,A,B出现的次数远远大于C,D出现的次数,此时这种编码方式就有一点浪费空间的意思。

在其他很多实际问题中也一样,所以有了哈夫曼树,当带权路径最短时,我们可以得到一颗综合搜索成本最低的树。

如何通过最大堆来生成哈夫曼树:

程序设计思路。
例子:通过最小堆来生成哈夫曼树(最大最小堆取决于需要取的数据权重)

1.需要从最小堆中去取出两个最小的结点,将他们挂在一个新的结点下。
2.将该结点的值附为两个结点的和。
3.将这个新生成的结点插入最小堆。

循环进行以上操作,我们最终可以得到一颗哈夫曼树。

//创建哈夫曼树
void makehuffman(void) {
	struct tree *t;
	struct tree *left;
	struct tree *right;
	
	while(length!=1){
		t = malloc(sizeof(LEN_TREE));
		left = Delete();
		//printf("left:%d\n", left->data);
		right = Delete();
		//printf("right:%d\n", right->data);
		t->left = left;
		t->right = right;
		t->data = left->data + right->data;
		Insert(*t);
	}

}
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最小堆最大堆都是二叉树的一种特殊形式。最小堆是指每个节点的值都小于或等于其子节点的值,而最大堆则相反,每个节点的值都大于或等于其子节点的值。最小堆的删除操作是删除最小值,也就是删除根节点。具体的操作是将最后一个节点替换到根节点的位置,然后自顶向下递归调整以满足最小堆的要求。最大堆的删除操作是删除最大值,也是先将最后一个节点提到根节点的位置,然后删除最大值,并将新的根节点放到适当的位置。 最小堆最大堆的优势是可以在常数时间内访问最小或最大值,而使用数组则需要遍历查找最小或最大值,时间复杂度至少为O(n)。但是,维护最小或最大堆的结构需要额外的工作,这也带来了复杂度的花销。总的来说,最小堆最大堆是一种对数据进行快速查找的数据结构。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [图解二叉堆(最小堆&最大堆)](https://blog.csdn.net/august5291/article/details/121120535)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [数据结构——最大堆最小堆](https://blog.csdn.net/qq_50675813/article/details/117753225)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值