Оглавление
4. Выберите два минимальных значения
1. Введение
Цель: использовать возможности C++ для решения проблем с указателями.
Примечание. Поскольку функциональные функции, такие как кодирование и декодирование, реализованы на основе инициализации, построения и обхода Хаффмана, они не будут показаны здесь, показаны только ключевые части построения Хаффмана и функции обхода, а ключевые части интерпретируются.
Отказ от ответственности: Автор вдохновлялся предшественниками, часть этого блога является интерпретацией кода предшественников, а часть получена автором с небольшими изменениями на основе кода предшественников.
Оригинальный авторский портал: создание дерева Хаффмана_Блог Reticent_Man-CSDN blog_Create Huffman tree
2. Узел
template<class T>
struct AlphaNode {
T data;
int freq=0;
string code;//编码
AlphaNode<T>* parent;
AlphaNode<T>* lchild;
AlphaNode<T>* rchild;
};
1. Используйте классы шаблонов: для последующего хранения пользовательских данных
2. частота: аббревиатура частоты. Поскольку входной конец кода предназначен для ввода любой строки и создания статистики, поэтому здесь сначала объявляется поле данных, используемое для хранения статистических результатов.
3.code: Результат кодирования (содержимое кодирования не будет отображаться в этом блоге)
4. Узлы имеют троичную древовидную структуру: корневой узел и левое и правое поддеревья.
3. Статистика персонажа
//字符串统计函数
//需要注意 这里是从arr_str[1]开始放东西的 arr_str[0]是空的
AlphaNode<char> arr_str[400];//建立一个节点数组
static int length = 1;
void statisticAlpha(string str) {
int i = 0;//从string[0]开始遍历
while (str[i]) {
if (0 == i) {
arr_str[i+1].data = str[i];
arr_str[i+1].freq++;
length++;
}
else {
int j = 1;//从新建数组的第二个存放数据的节点开始遍历
//判定是否有重复的数据
for (; arr_str[j].data!=NULL; j++) {
if (str[i] == arr_str[j].data)break;
}
//锁定应该存放东西的位置 即j位置
if (j >length-1) {
arr_str[length].data = str[i];
arr_str[length].freq++;
length++;
}
else {
arr_str[j].freq++;
}
}
i++;
}
int j = 1;
cout << "您输入的字符串 统计为:" << endl;
while (arr_str[j].data!=NULL) {
cout << arr_str[j].data << ':' << arr_str[j].freq << endl;
j++;
}
}
//注意 以下代码只是示例
main(){
cout <<"请在下方输入一个任意字符串"<< endl;
string temp;
getline(cin, temp);
statisticAlpha(temp);
}
1. Используйте temp в функции main() для временного сохранения (последний пример)
При чтении необходимо читать пробелы и заканчивать символом возврата каретки, поэтому используется функция getline(cin,str).
2. Специальное решение: (0==i) сохраняется напрямую, потому что первый символ не должен совпадать с ним.
3.
//判定是否有重复的数据
for (; arr_str[j].data!=NULL; j++) {
if (str[i] == arr_str[j].data)break;
}
//锁定应该存放东西的位置 即j位置
Так как str[] — это массив, временно используемый для хранения, он не будет временно меняться, поэтому поместите его впереди.
Когда результат сравнивается, поскольку массив начинает хранить вещи с 1, результат цикла j можно непосредственно использовать ниже, потому что нормальная позиция [j] пуста.
4. Далее следует определить, достиг ли j конца, а затем выполнить ряд связанных операций.
4. Выберите два минимальных значения
【Ключевые моменты】
1. Минимальное значение меняется по мере роста массива, а сумма новых узлов (частота двух дочерних) также будет включена в диапазон расчета
например: первый раз: 2 1 1
Второй раз: 2 1 1 2 (2 в конце=1+1) (сумма двух дочерних элементов с наименьшим весом (частота))
Третий раз: 2 1 1 2 4 (4 в конце = 2+2) (Поскольку две единицы использовались, минимальный вес равен череду старой 2 и новой 2)
2. Как определить, является ли узел уже дочерним?
Ввести часового p[200];
int p[200] = { 0 };//用来判断节点是否被选取过
Сначала все часовые инициализируются в 0, что указывает на то, что они не были выбраны.
3.
void Huffman::SelectMin(int &x, int &y,int end) {
//x、y用于返回两个 最小值权值的 位置
long int min1 = 999999;
for (int i = 1; i <= end; i++) {
if (a[i].freq < min1 && p[i] == 0) {
min1 = a[i].freq;
x = i;
}
}
min1 = 999999;
for (int i = 1; i <= end; i++) {
if (a[i].freq < min1 && i != x && p[i] == 0) {
min1= a[i].freq;
y = i;
}
}
}
Обратите внимание, что в двух специальных суждениях есть p[i]==0, // чтобы гарантировать, что они не выбраны
Определите минимальное значение, инициализированное до 999999, чтобы гарантировать, что минимальное значение будет сгенерировано
4. Зачем передавать ссылки int &x, int &y
Удобно изменять данные в любое время, потому что расположение более поздней части кода другое, и область действия другая, поэтому во избежание проблем используется эталонный метод.
5. Что такое конец
Как упоминалось в 1. выше: длина сравнения будет продолжать расти. На этот раз конец будет передан в исходной длине позже, но в следующий раз он будет передан в длине +1 (поскольку создается новый узел, новый узел должен быть включен в сравнение), поэтому конец является числом переменной длины. .
5. Построить дерево Хаффмана
//示例:避免看不懂在写什么
class Huffman{
public:
AlphaNode<char>* Creation(int);//创建哈夫曼树
private:
AlphaNode<char>* a;
}
int x, y;
//构建的函数正文:
AlphaNode<char>* Huffman::Creation(int n) {
int i;
//初始化
for (int i = 1; i <= 2 * n - 1; i++) {
a[i].lchild = a[i].parent = a[i].rchild = NULL;
}
for (i = n; i < 2 * n - 2; i++) {
SelectMin(x, y, i-1);
p[x] = p[y] = 1;
a[i].lchild = &a[x];
a[i].rchild = &a[y];
a[i].freq = a[x].freq + a[y].freq;
}
return &a[i - 1];
}
//示例:
main(){
AlphaNode<char> *p = hf.Creation(length);
//length就是存储之后的存储数组的长度 比如abca的长度为4(从1开始存 +1)
}
Параметр int n функции — это длина массива (передается в следующем примере).
Сосредоточьтесь на объяснении средней части кода:
1. SelectMin(x, y, i-1);
Определите положение узла минимального веса и верните его как x, y, где вес узла x ≤ вес узла y
(по умолчанию x — левый узел)
2. (я < 2*n - 2)
Почему это суд?
Количество раз этого цикла должно быть строго ограничено
Потому что в последнем цикле всегда будут суммироваться два предпоследних и второго по величине веса, и это суммирование будет генерировать новую точку хранения, а именно:
a[i].freq = a[x].freq + a[y].freq;
Приговор все равно будет работать.
Если количество раз на один больше, это приведет к тому, что значение y будет неопределенным, что приведет к бесконечному циклу.
(Поскольку ссылка на y передается, а функция SelectMin не указывает, что произойдет, если «y не имеет соответствующего значения», в результате y будет продолжать использовать исходное значение адреса для продолжения цикла for)
количество петель = длина массива - 1;
Длина массива после подсчета строки abca равна 3 (объединение похожих элементов), тогда количество сравнений 3-1=2.
В коде количество сравнений равно (2*n - 2 - n)+1=n - 1.
3. Почему возвращается &a[i - 1];?
Потому что по такому алгоритму: сразу за массивом открыть пространство для хранения вещей
Передняя часть массива (a[1] ~a[n]) — это все сохраненные данные без дочерних и корневых узлов.
Арифметика указателей доступна только из [n+1] с соединениями между дочерними и корневыми узлами.
А конец этого массива, то есть последний узел взвешенной суммы, является головным узлом.
Головной узел хранит соответствующие дочерние узлы.
Так что прямо верните его адрес.
6. Функция обхода
void Huffman::DispTree(AlphaNode<char>* a) {
if (a != NULL) {
cout << a->data;
if (a->lchild != NULL || a->rchild != NULL) {
cout << '(';
DispTree(a->lchild);
if (a->rchild != NULL)cout << ',';
DispTree(a->rchild);
cout << ')';
}
}
}
Цитирую оригинального автора:
Позже было обнаружено, что выходная форма чистых чисел не может видеть соответствующую связь между поддеревом и родителями. Читая книгу о структурах данных, я увидел обобщенную таблицу, поэтому подумал использовать метод добавления скобок, то есть сначала судить о том, есть ли у узла левое поддерево (в дереве Хаффмана, если есть левое поддерево, должно быть правое поддерево), если есть, выведите скобки, а левое и правое поддеревья разделите запятыми. Это увеличивает визуальную читаемость.
7. Исходный код
#include<iostream>
#include<string>
using namespace std;
template<class T>
struct AlphaNode {
T data;
int freq=0;
string code;//编码
AlphaNode<T>* parent;
AlphaNode<T>* lchild;
AlphaNode<T>* rchild;
};
AlphaNode<char> arr_str[400];
int p[200] = { 0 };//用来判断节点是否被选取过
void statisticAlpha(string str);
int x, y;
struct HCode {
char data;
string code;
};
class Huffman {
private:
AlphaNode<char>* a;
int N;//叶子节点数量
void code(int i, string newcode);//递归函数 对应第i个节点编码
public:
Huffman(AlphaNode<char>*);
AlphaNode<char>* Creation(int);//创建哈夫曼树
void DispTree(AlphaNode<char>*);//遍历
void CreateCodeTable();//创建编码表
void Encode(char *s, char *d);//编码
void Decode(char *s, char *d);//译码
void SelectMin( int &x, int &y, int end);
};
Huffman::Huffman(AlphaNode<char>* temp) {
a = temp;
}
//字符串统计函数
//需要注意 这里是从arr_str[1]开始放东西的 arr_str[0]是空的
static int length = 1;
void statisticAlpha(string str) {
int i = 0;//从string[0]开始遍历
while (str[i]) {
if (0 == i) {
arr_str[i+1].data = str[i];
arr_str[i+1].freq++;
length++;
}
else {
int j = 1;//从新建数组的第二个存放数据的节点开始遍历
for (; arr_str[j].data!=NULL; j++) {
if (str[i] == arr_str[j].data)break;
}
//锁定应该存放东西的位置
if (j >length-1) {
arr_str[length].data = str[i];
arr_str[length].freq++;
length++;
}
else {
arr_str[j].freq++;
}
}
i++;
}
int j = 1;
cout << "您输入的字符串 统计为:" << endl;
while (arr_str[j].data!=NULL) {
cout << arr_str[j].data << ':' << arr_str[j].freq << endl;
j++;
}
}
//返回统计后的数组的长度
//挑选两个最小值的函数
void Huffman::SelectMin(int &x, int &y,int end) {
//x、y用于返回两个 最小值权值的 位置
long int min1 = 999999;
for (int i = 1; i <= end; i++) {
if (a[i].freq < min1 && p[i] == 0) {
min1 = a[i].freq;
x = i;
}
}
min1 = 999999;
for (int i = 1; i <= end; i++) {
if (a[i].freq < min1 && i != x && p[i] == 0) {
min1= a[i].freq;
y = i;
}
}
}
//abbcccddddeeeeeffffffggggggg
AlphaNode<char>* Huffman::Creation(int n) {
int i;
//初始化
for (int i = 1; i <= 2 * n - 1; i++) {
a[i].lchild = a[i].parent = a[i].rchild = NULL;
}
for (i = n; i < 2 * n - 2; i++) {
SelectMin(x, y, i-1);
p[x] = p[y] = 1;
a[i].lchild = &a[x];
a[i].rchild = &a[y];
a[i].freq = a[x].freq + a[y].freq;
}
return &a[i - 1];
}
void Huffman::DispTree(AlphaNode<char>* a) {
if (a != NULL) {
cout << a->data;
if (a->lchild != NULL || a->rchild != NULL) {
cout << '(';
DispTree(a->lchild);
if (a->rchild != NULL)cout << ',';
DispTree(a->rchild);
cout << ')';
}
}
}
int main() {
cout <<"请在下方输入一个任意字符串"<< endl;
string temp;
getline(cin, temp);
statisticAlpha(temp);
cout << length << endl;
Huffman hf(arr_str);
AlphaNode<char> *p = hf.Creation(length);
hf.DispTree(p);
return 0;
}