1. 程式人生 > >哈夫曼樹 荷馬史詩-洛谷

哈夫曼樹 荷馬史詩-洛谷

這道題目做了五個多小時,主要還是不瞭解哈夫曼樹的一些細節問題,自己做個總結吧。

題目連結:https://www.luogu.org/problemnew/show/P2168

總結:

一:這道題目的一些收穫

1:求最終編碼文字的長度,不一定必須讓字元組成的編碼的長度資訊必須儲存在葉子結點。可以在建立的時候,定義一個ans=0,在從下向上建立哈夫曼樹的時候,不斷的相加。比如,有個高度為h的結點,他的結點帶權路徑長度(不懂定義看二的概念),可以是h個w(權重)相加。這在建樹的過程中可以實現。本題目就是採用的這種方式

2:結點的選擇:選擇權值最小,並且高度較小的。(權值較小的可以理解,那麼高度較小呢?題目中說了,求的是最長字串 si 的最短長度,有兩個權值相同的結點,將h更大的結點,留到後面,會讓最長字串的最短長度更小)當時自己就是錯在這裡,火來參考了大神的程式碼

3:***新增空結點

當時多進位制的時候。因為每次都是將k個節點合併為1個(減少k-1個),一共要將n個節點合併為1個,如果(n-1)%(k-1)!=0 則最後一次合併時不足k個。也就表明了最靠近根節點的位置反而沒有被排滿,因此我們需要加入k-1-(n-1)%(k-1)個空節點使每次合併都夠k個節點(也就是利用空節點將其餘的節點擠到更優的位置上)。

二:一些概念

  1. 結點帶權路徑長度:L*W(W是這個結點的權值,L是這個結點的深度,根節點的深度是0)。
  2. 樹的帶權路徑長度:所有葉子結點的結點帶去按路徑長度之和。

三:思想:

 自底向上,每次選擇最小的兩個結點,合成一個結點。

四:特點:

  1. 不存在度為1的結點
  2. n個結點的哈夫曼樹總共需要結點2n-1,所以對於二進位制的編碼,開的空間最小是2n-1。
  3. 順序儲存,存在結構體裡面挺好。
#include <bits/stdc++.h>
using namespace std;
#define ll long long 
struct Node{
	ll cnt,h;
	Node(ll CNT,ll H){
		cnt=CNT,h=H;
	}
};
bool operator<(const Node a,const Node b){
	if(a.cnt!=b.cnt) return a.cnt>b.cnt;
	else return a.h>b.h;
} 
priority_queue<Node> q;
int main()
{
	int n,len,i,j;
	cin>>n>>len;
	for(i=0;i<n;i++){
		ll temp;
		scanf("%lld",&temp);
		q.push(Node(temp,1));
	}
	if(0!=((n-1)%(len-1))){
		int add=len-1-(n-1)%(len-1);
		for(i=0;i<add;i++)  q.push(Node(0,1));
		n+=add;
	}
	ll ans=0;
	while(q.size()>1)
	{
		ll sum=0,maxh=0;
		for(i=0;i<len;i++){
			maxh=max(q.top().h,maxh);
			sum+=q.top().cnt;	
			q.pop();
		}
		ans+=sum;
		q.push(Node(sum,maxh+1));
	}
	printf("%lld\n%lld",ans,q.top().h-1);
	return 0;
}