前書き
ハフマンツリーは、文字をエンコードするためのデータ構造であり、文字の使用頻度に応じて文字のバイナリ表現を決定できるため、変換後のバイナリ列をできるだけ短くすることができます。
ハフマンツリーの具体的な紹介については、このブログを参照して、漫画でハフマンツリーを紹介してください。
次に、ハフマンツリーの作成方法を紹介します。n個の文字とそれに対応する頻度(出現数)を指定して、ハフマンエンコード後のエンコードされた文字列の長さを見つけます。
といった:
4 //字符个数
a 1
b 2
c 3
d 4
19を返します。(カウント方法がわからない場合は、上記のリンクのブログを参照してください)
c ++の構造
方法1
まず、優先度キューを使用して、頻度情報を含むノードをキューに追加し、2つの最小ノードaとbを毎回キューから取り出し、頻度の合計を求め、新しい子ノードを構築します。 b、新しいノードをキューに追加します。キューにノードが1つだけ残るまで、このプロセスを繰り返します。
上記の手順では、ハフマンツリーの構築プロセスをシミュレートし、実際にツリーを構築します。次に、ツリーのルートノードをトラバースし、各リーフノードを見つけて、その値(周波数)と深度(つまり、コード長)の積の合計が必要な結果です。
時間の複雑さ:O(nlogn)
ps:このコードはポインターを解放しませんが、これは重要ではないため、無視されます。
#include<iostream>
#include<queue>
#include<unordered_map>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
struct Node {
long long val;
Node* left, *right;
Node(long long val) {
this->val = val;
left = NULL;
right = NULL;
}
};
struct cmp {
bool operator()(const Node* a, const Node* b) const{
return a->val > b->val;
}
};
long long search(Node* root, long long level) {
if (root == NULL) return 0;
if (root->left == NULL && root->right == NULL) return root->val * level;
return search(root->left, level + 1) + search(root->right, level + 1);
}
int main() {
long long n;
cin >> n;
if (n == 0) {
cout << 0;
return 0;
}
long long arr[n];
char c;
for (long long i = 0; i < n; ++i) {
cin >> c >> arr[i];
}
if (n == 1) {
cout << arr[0];
return 0;
}
priority_queue<Node*, vector<Node*>, cmp> pq;
for (long long i = 0; i < n; ++i) {
//if (arr[i] <= 0) continue;
Node* t = new Node(arr[i]);
pq.push(t);
}
if (pq.size() == 1) {
cout << pq.top()->val << endl;
return 0;
}
Node* root;
while(pq.size() > 1) {
Node* a = pq.top();
pq.pop();
Node* b = pq.top();
pq.pop();
Node* sum = new Node(a->val + b->val);
sum->left = a;
sum->right = b;
root = sum;
pq.push(sum);
}
cout << search(root, 0) << endl;
}
方法2
プライオリティキューを使用したくない場合や、比較関数を自分で作成したくない場合は、どのように記述できますか?
ルールに注意してください。最小の2つのノードがキューから取り出され、値によって追加されるたびに、新しいノードの値が増加します。通常の配列のみを使用して元のノードを格納し、別の配列を使用するとします新しいノードを格納するには、2つのポインターを使用して2つの配列の添え字をポイントします。これは、特定の制御(左の配列から取得するか、右の配列から取得)を通じて実行できます。詳細はコードをご覧ください。
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
int n;
vector<long long> a[2];
int idx[2];
inline long long popleft(int i) {
return a[i][idx[i]++];
}
inline long long getmin() {
if (idx[0] >= (int)a[0].size()) {
return popleft(1);
}
if (idx[1] >= (int)a[1].size()) {
return popleft(0);
}
if (a[0][idx[0]] < a[1][idx[1]]) {
return popleft(0);
}
return popleft(1);
}
int main() {
cin >> n;
if (n == 0) {
cout << 0 << endl;
return 0;
}
long long t1, t2;
char c;
for (int i = 0; i < n; ++i) {
cin >> c >> t2;
a[0].push_back(t2);
}
if (n == 1) {
cout << t2 << endl;
return 0;
}
sort(a[0].begin(), a[0].end());
long long ans = 0;
for (int i = 1; i < n; ++i) {
t1 = getmin();
t2 = getmin();
ans += t1 + t2;
a[1].push_back(t1 + t2);
}
cout << ans << endl;
return 0;
}
時間の複雑さはまだO(nlogn)です。