C++ пишет дерево Хаффмана с указателем

Оглавление

1. Введение

2. Узел

3. Статистика персонажа

4. Выберите два минимальных значения

5. Построить дерево Хаффмана

6. Функция обхода

7. Исходный код


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;
}

Guess you like

Origin blog.csdn.net/Samurai_Bushido/article/details/124889615
C++
Recommended