C语言---数据结构实验---哈夫曼树及哈夫曼编码的算法实现---图的基本操作

这篇具有很好参考价值的文章主要介绍了C语言---数据结构实验---哈夫曼树及哈夫曼编码的算法实现---图的基本操作。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

写在前面

本篇实验代码非本人写,代码源自外部,经调试解决了部分warning和error后在本地vs上可以正常运行,如有运行失败可换至vs

未来会重构实现该两个实验


哈夫曼树及哈夫曼编码的算法实现

实验内容

内容要求:

1、初始化(Init):能够对输入的任意长度的字符串s进行统计,统计每个字符的频度,并建立哈夫曼树
2、建立编码表(CreateTable):利用已经建好的哈夫曼树进行编码,并将每个字符的编码输出。
3、编码(Encoding):根据编码表对输入的字符串进行编码,并将编码后的字符串输出。
4、译码(Decoding):利用已经建好的哈夫曼树对编码后的字符串进行译码,并输出译码结果。

测试数据:
输入字符串“thisprogramismyfavourite”,完成这28个字符的编码和译码。

代码实现

#include<iostream>
#include<string.h>
#include<queue>
#define MAX 10000 
using namespace std;
char a[100], buff[1024], p;
typedef struct
{
	char letter, * code;
	int weight;
	int parent, lchild, rchild;
}HTNode, * HuffmanTree;

int n;
char coding[100];

int Min(HuffmanTree& HT, int i)
{
	int j;
	int k = MAX;
	int flag=0;
	for (j = 0; j <= i; ++j)
	{
		if (HT[j].weight < k && HT[j].parent == 0)
		{
			k = HT[j].weight;
			flag = j;
		}
	}
	HT[flag].parent = 1;
	return flag;
}

void Select(HuffmanTree& HT, int i, int& s1, int& s2)
{
	s1 = Min(HT, i);
	s2 = Min(HT, i);
}

void CreateHuffmanTree(HuffmanTree& HT, char t[], int w[])
{
	int m;
	int i, s1, s2;
	if (n <= 1)
		return;
	m = 2 * n - 1; 
	HT = new HTNode[m + 1];
	for (i = 0; i < n; i++)
	{
		char arr[] = "0";
		char* pa = arr;
		HT[i].code = pa;
		HT[i].parent = 0;
		HT[i].lchild = -1;
		HT[i].rchild = -1;
		HT[i].letter = t[i];
		HT[i].weight = w[i];
	}
	for (i = n; i <= m; i++)
	{
		char arr[] = "0";
		char* pa = arr;
		HT[i].code = pa;
		HT[i].parent = 0;
		HT[i].lchild = -1;
		HT[i].rchild = -1;
		HT[i].letter = ' ';
		HT[i].weight = 0;
	}
	cout << "********************************" << endl;
	for (i = n; i < m; i++)
	{
		Select(HT, i - 1, s1, s2);

		HT[s1].parent = i;
		HT[s2].parent = i;
		HT[i].lchild = s1;
		HT[i].rchild = s2;
		HT[i].weight = HT[s1].weight + HT[s2].weight;
	}
}

void CreatHuffmanCode(HuffmanTree HT)
{
	int start, c, f;
	int i;
	char* cd = new char[n];
	cd[n - 1] = '\0';
	cout << "字符编码为:" << endl;
	for (i = 0; i < n; i++)
	{
		start = n - 1;
		c = i;
		f = HT[i].parent;
		while (f != 0) 
		{
			--start;
			if (HT[f].lchild == c) 
			{
				cd[start] = '0';
			}
			else 
			{
				cd[start] = '1';
			}
			c = f;
			f = HT[f].parent;
		}
		HT[i].code = new char[n - start];
		strcpy(HT[i].code, &cd[start]);
		cout << HT[i].letter << ":" << HT[i].code << endl;
	}
	delete[] cd;
}

void HuffmanTreeDecode(HuffmanTree HT, char cod[], int b)  
{
	char sen[100];
	char temp[50];
	char voidstr[] = " ";
	int t = 0;
	int s = 0;
	int count = 0;
	for (int i = 0; i < b; i++)
	{
		temp[t++] = cod[i];
		temp[t] = '\0';
		for (int j = 0; j < n; j++) 
		{
			if (!strcmp(HT[j].code, temp)) 
			{
				sen[s] = HT[j].letter;
				s++;
				count += t;
				strcpy(temp, voidstr);
				t = 0;
				break;
			}
		}
	}
	if (t == 0) 
	{
		sen[s] = '\0';
		cout << "译码为:" << endl;
		cout << sen << endl;
	}
	else 
	{
		cout << "二进制源码有错!从第" << count + 1 << "位开始" << endl;
	}
}

int main()
{
	HuffmanTree HT;
	int b[100]={0};
	int i, j;
	int symbol = 1, x, k;
	cout << "请输入一段文字:";
	cin >> buff;
	int len = (int)strlen(buff);
	for (i = 0; i < len; i++)
	{
		for (j = 0; j < n; j++)
		{
			if (a[j] == buff[i])
			{
				b[j] = b[j] + 1;
				break;
			}
		}
		if (j >= n)
		{
			a[n] = buff[i];
			b[n] = 1;
			n++;
		}
	}
	cout << "字符和权值信息如下" << endl;
	for (i = 0; i < n; i++)
	{
		cout << "字符:" << a[i] << "  权值:" << b[i] << endl;
	}
	CreateHuffmanTree(HT, a, b);
	CreatHuffmanCode(HT);
	cout << "文字编码为:\n";
	for (int i = 0; i < len; i++)
	{
		for (int j = 0; j < n; j++)
		{
			if (buff[i] == HT[j].letter)
			{
				cout << HT[j].code;
				break;
			}
		}
	}
	cout << "\n译码:" << endl;
	while (1)
	{
		cout << "请输入要译码的二进制字符串,输入'#'结束:";
		x = 1;
		k = 0; 
		symbol = 1;
		while (symbol) 
		{
			cin >> p;
			if (p != '1' && p != '0' && p != '#') 
			{
				x = 0;
			}
			coding[k] = p;
			if (p == '#')
				symbol = 0;
			k++;
		}
		if (x == 1) 
		{
			HuffmanTreeDecode(HT, coding, k - 1);
		}
		else 
		{
			cout << "有非法字符!" << endl;
		}
		cout << "是否继续?(Y/N):";
		cin >> p;
		if (p == 'y' || p == 'Y')
			continue;
		else
			break;
	}
	return 0;
}

图的基本操作

实验内容

分别用邻接矩阵和邻接表对如下有向图实现:
1.输出存储结果;
2.计算各结点的出度和入度,并输出;
3.实现图的深度优先遍历和广度优先遍历,并输出。

C语言---数据结构实验---哈夫曼树及哈夫曼编码的算法实现---图的基本操作,简单实验,c语言,数据结构文章来源地址https://www.toymoban.com/news/detail-638217.html

代码实现

#include <iostream>
#include <vector>
#include <map>
#include <queue>
#include <stack>
using namespace std;

namespace matrix
{
	// 顶点的类型,权值的类型,权值的最大值,是否是有向图
	template<class V, class W, W W_MAX = INT_MAX, bool Direction = true>
	class Graph
	{
	public:
		Graph() = default;
		Graph(const V* vertexs, size_t n)
		{
			// 建立顶点和下标的映射关系
			for (size_t i = 0; i < n; i++)
			{
				_vertexs.push_back(vertexs[i]);
				_vIndexMap[vertexs[i]] = i;
			}

			_matrix.resize(n, vector<W>(n, W_MAX));
		}

		// 给一个顶点取出来它对应的下标
		size_t GetVertexsIndex(const V& vertex)
		{
			auto it = _vIndexMap.find(vertex);
			if (it != _vIndexMap.end())
			{
				return it->second;
			}
			else
			{
				throw invalid_argument("不存在的顶点");
				return -1;
			}
		}

		// 给邻接矩阵加值
		void _AddEdge(size_t srci, size_t dsti, W w)
		{
			_matrix[srci][dsti] = w;
			if (Direction == false)
				_matrix[dsti][srci] = w;
		}

		// 加边
		void AddEdge(const V& src, const V& dst, W w)
		{
			size_t srci = GetVertexsIndex(src);
			size_t dsti = GetVertexsIndex(dst);
			_AddEdge(srci, dsti, w);
		}

		// 显示邻接矩阵
		void Print()
		{
			cout << "  ";
			for (size_t i = 0; i < _vertexs.size(); ++i)
			{
				cout << i << " ";
			}
			cout << endl;
			// 打印矩阵
			for (size_t i = 0; i < _matrix.size(); ++i)
			{
				cout << i << " ";
				for (size_t j = 0; j < _matrix[i].size(); ++j)
				{
					if (_matrix[i][j] != W_MAX)
						cout << _matrix[i][j] << " ";
					else
						cout << "#" << " ";
				}
				cout << endl;
			}
			// 打印所有的边
			for (size_t i = 0; i < _matrix.size(); ++i)
			{
				for (size_t j = 0; j < _matrix[i].size(); ++j)
				{
					if (_matrix[i][j] != W_MAX)
					{
						cout << _vertexs[i] << "-" << _vertexs[j] << endl;
					}
				}
			}
		}

		// 计算结点的入度和出度
		void cal_inward_outward()
		{
			for (int i = 0; i < _matrix.size(); i++)
			{
				int inward = 0, outward = 0;
				for (int j = 0; j < _matrix[i].size(); j++)
				{
					if (_matrix[j][i] != W_MAX)
						inward++;
					if (_matrix[i][j] != W_MAX)
						outward++;
				}
				cout << _vertexs[i] << "的入度为:" << inward << " " << "出度为" << outward << endl;
			}
		}

		// 图的BFS遍历,从某个顶点开始遍历
		void BFS(const V& Vertex)
		{
			queue<V> qe;
			qe.push(Vertex);
			size_t d = 1;
			vector<bool> visited(_matrix.size(), false);
			while (!qe.empty())
			{
				size_t dsize = qe.size();
				cout << Vertex << "的" << d << "度好友:";
				while (dsize--)
				{
					V front = qe.front();
					qe.pop();
					size_t index = GetVertexsIndex(front);
					visited[index] = true;
					for (size_t i = 0; i < _matrix.size(); i++)
					{
						if (_matrix[index][i] != W_MAX && visited[i] == false)
						{
							//找到了
							visited[i] = true;
							cout << _vertexs[i] << " ";
							qe.push(_vertexs[i]);
						}
					}
				}
				cout << endl;
				d++;
			}
		}

		// 图的DFS遍历,从某个顶点开始遍历
		void DFS(const V& Vertex)
		{
			size_t index = GetVertexsIndex(Vertex);
			vector<bool> visited(_vertexs.size(), false);
			_DFS(index, visited);
		}

		void _DFS(size_t index, vector<bool>& visited)
		{
			visited[index] = true;
			cout << _vertexs[index] << " ";
			for (size_t i = 0; i < _vertexs.size(); i++)
			{
				if (_matrix[index][i] != W_MAX && visited[i] == false)
				{
					_DFS(i, visited);
				}
			}
		}

	private:
		// 下标到顶点的映射
		vector<V> _vertexs;
		// 邻接矩阵的存储
		vector<vector<W>> _matrix;
		// 顶点到下标的映射
		map<V, size_t> _vIndexMap;
	};
}

namespace list_table
{
	// 对于边的结构来说,需要存储的有边的两个顶点,边的权值,以及链接属性
	template <class W>
	struct Edge
	{
		Edge() = default;
		Edge(size_t srci, size_t dsti, W w)
			:_srci(srci)
			, _dsti(dsti)
			, _w(w)
			, _next(nullptr)
		{}
		size_t _srci;
		size_t _dsti;
		W _w;
		Edge<W>* _next;
	};

	// 顶点的类型,权值的类型,权值的最大值,是否是有向图
	template<class V, class W, bool Direction = true>
	class Graph
	{
		typedef Edge<W> Edge;
	public:
		Graph() = default;
		Graph(const V* vertexs, size_t n)
		{
			// 建立顶点和下标的映射关系
			for (size_t i = 0; i < n; i++)
			{
				_vertexs.push_back(vertexs[i]);
				_vIndexMap[vertexs[i]] = i;
			}

			_link_tables.resize(n, nullptr);
		}

		// 给一个顶点取出来它对应的下标
		size_t GetVertexsIndex(const V& vertex)
		{
			auto it = _vIndexMap.find(vertex);
			if (it != _vIndexMap.end())
			{
				return it->second;
			}
			else
			{
				throw invalid_argument("不存在的顶点");
				return -1;
			}
		}

		// 给邻接表加值
		void _AddEdge(size_t srci, size_t dsti, W w)
		{
			// 创建一个边的结构
			Edge* EdgePtr = new Edge(srci, dsti, w);
			Edge* tail = _link_tables[srci];
			while (tail && tail->_next)
				tail = tail->_next;
			// 把这个边的结构头插链入到邻接表
			if (tail == nullptr)
			{
				_link_tables[srci] = EdgePtr;
			}
			else
			{
				tail->_next = EdgePtr;
				tail = tail->_next;
			}
		}

		// 加边
		void AddEdge(const V& src, const V& dst, W w)
		{
			size_t srci = GetVertexsIndex(src);
			size_t dsti = GetVertexsIndex(dst);
			_AddEdge(srci, dsti, w);
			// 如果是无向图,就把对应的边也加到邻接表中
			if (Direction == false)
			{
				_AddEdge(dsti, srci, w);
			}
		}

		// 显示邻接矩阵
		void Print()
		{
			// 打印邻接表
			for (size_t i = 0; i < _link_tables.size(); ++i)
			{
				cout << _vertexs[i] << " ";
				Edge* cur = _link_tables[i];
				while (cur)
				{
					cout << "[" << _vertexs[cur->_srci] << "->" << _vertexs[cur->_dsti] << "]" << " ";
					cur = cur->_next;
				}
				cout << endl;
			}
		}
	private:
		// 下标到顶点的映射
		vector<V> _vertexs;
		// 邻接表的存储
		vector<Edge*> _link_tables;
		// 顶点到下标的映射
		map<V, size_t> _vIndexMap;
	};
}

// 创建邻接矩阵和邻接表
void CreateGraph(matrix::Graph<int, int>& mph, list_table::Graph<int, int>& lph)
{
	cout << "输入顶点个数->";
	int vernum = 0;
	cin >> vernum;
	cout << "输入" << vernum << "个顶点->";
	vector<int> vertex(vernum);
	for (int i = 0; i < vernum; i++)
		cin >> vertex[i];
	matrix::Graph<int, int> tmp_mph(vertex.data(), vertex.size());
	list_table::Graph<int, int> tmp_lph(vertex.data(), vertex.size());
	cout << "输入要添加的边的个数->";
	int edgenum = 0;
	cin >> edgenum;
	cout << "输入" << edgenum << "条边->" << endl;
	int srci = 0, dsti = 0;
	vector<pair<int, int>> edge(edgenum);
	for (int i = 0; i < edgenum; i++)
	{
		cin >> srci >> dsti;
		tmp_mph.AddEdge(srci, dsti, 0);
		tmp_lph.AddEdge(srci, dsti, 0);
	}
	mph = tmp_mph;
	lph = tmp_lph;
}

// 输出存储结果
void PrintInfo(matrix::Graph<int, int> mph, list_table::Graph<int, int> lph)
{
	cout << "邻接矩阵存储结果" << endl;
	mph.Print();
	cout << endl;
	cout << "邻接表的存储结果" << endl;
	lph.Print();
	cout << endl;
	cout << "各顶点的入度和出度" << endl;
	mph.cal_inward_outward();
	cout << endl;
}

// 输出遍历结果
void PrintOrder(matrix::Graph<int, int> mph, list_table::Graph<int, int> lph)
{
	int vertex = 0;
	cout << "输入要进行遍历的顶点" << endl;
	cin >> vertex;
	cout << "图的深度优先遍历:" << endl;
	mph.DFS(vertex);
	cout << endl;
	cout << "图的广度优先遍历:" << endl;
	mph.BFS(vertex);
	cout << endl;
}

int main()
{
	matrix::Graph<int, int> mph;
	list_table::Graph<int, int> lph;
	CreateGraph(mph, lph);
	PrintInfo(mph, lph);
	PrintOrder(mph, lph);
	return 0;
}

到了这里,关于C语言---数据结构实验---哈夫曼树及哈夫曼编码的算法实现---图的基本操作的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构“基于哈夫曼树的数据压缩算法”的实验报告

    一个不知名大学生,江湖人称菜狗 original author: jacky Li Email : 3435673055@qq.com Last edited: 2022.11.20 目录 数据结构“基于哈夫曼树的数据压缩算法”的实验报告 一、实验目的 二、实验设备 三、实验内容 1.【问题描述】 2.【输入要求】 3.【输出要求】 4.【实验提示】 四、实验步骤

    2024年02月09日
    浏览(61)
  • 【数据结构--哈夫曼编码(C语言版)】

    哈夫曼树 介绍哈夫曼树前先介绍下面几个名词: 1. 结点的路径长度 l 从根结点到该结点的路径上分支的数目 ,如下图结点 a 的 l = 3 。 2. 树的路径长度 树中所有叶子结点的路径长度之和 ,如下图该树的路径长度为 2 + 3 + 3 + 2 + 2 。 3. 结点的权 w 给每一个结点赋予一个新的数

    2024年02月04日
    浏览(50)
  • (数据结构)哈夫曼编码实现(C语言)

    哈夫曼的编码:从一堆数组当中取出来最小的两个值,按照左下右大的进行绘制,将两个权值之和,放入队列当中,然后再进行取出两个小的,以此类推,直到全部结束,在根据图根节点,到叶子节点,每一个分支来得出编码,向左0,向右1,即可得到一个结果。

    2024年02月16日
    浏览(53)
  • 数据结构 实验17:Huffman树和Huffman编码——学习理解哈夫曼树

    目录 前言 实验要求 算法描述 个人想法 代码实现和思路、知识点讲解 知识点讲解 文件传输 Huffman树的存储 Huffman的构造  Huffman编码 编码和译码 代码实现 文件写入和输出 Huffman树初始化 构造Huffman树 求带权路径长度 Huffman编码 Huffman译码 结束 代码测试 测试结果 利用Huffman编

    2024年02月03日
    浏览(60)
  • 数据结构与算法(C语言版)---哈夫曼编译码器

    1.1、问题阐述 利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道) ,每端都需要一个完整的编/译

    2024年02月03日
    浏览(89)
  • 数据结构——哈夫曼树与哈夫曼编码

    1.1 基本概念 路径 :指从根结点到该结点的分支序列。 路径长度 :指根结点到该结点所经过的分支数目。 结点的带权路径长度 :从树根到某一结点的路径长度与该结点的权的乘积。 树的带权路径长度(WPL) :树中从根到所有叶子结点的各个带权路径长度之和。 哈夫曼树 是由

    2024年02月05日
    浏览(45)
  • 数据结构之哈夫曼树和哈夫曼编码

    切入正题之前,我们先了解几个概念: 路径:从树的一个结点到另一个结点分支所构成的路线 路径长度:路径上的分支数目 树的路径长度:从根结点出发到每个结点的路径长度之和 带权路径长度:该结点到根结点的路径长度乘以该结点的权值 树的带权路径长度:树中所有

    2024年02月11日
    浏览(45)
  • 数据结构之哈夫曼树与哈夫曼编码

    编码是信息处理的基础(重新表示信息)。 普通的编码是等长编码,例如7位的ASCIL编码,对出现频率不同的字符都使用相同的编码长度。 但其在传输和存储等情况下编码效率不高 。 可使用不等长编码,来压缩编码:高频字符编码长度更短,低频字符编码长度更长。   [例

    2023年04月15日
    浏览(64)
  • 【数据结构与算法】-哈夫曼树(Huffman Tree)与哈夫曼编码

    超详细讲解哈夫曼树(Huffman Tree)以及哈夫曼编码的构造原理、方法,并用代码实现。 路径 :从树中一个结点到另一个结点之间的 分支 构成这两个结点间的路径。 结点的路径长度 :两结点间路径上的 分支数 。 树的路径长度: 从树根到每一个结点的路径长度之和。记作: TL  权

    2024年02月06日
    浏览(53)
  • 数据结构-哈夫曼树

    介绍 哈夫曼树,指 带权路径长度最短的二叉树 ,通常用于数据压缩中 什么是带权路径长度? 假设有一个结点,我们为它赋值,这个值我们称为权值,那么从根结点到它所在位置,所经历的路径,与这个权值的积,就是它的带权路径长度。 比如有这样一棵树,D权值为2  从

    2024年02月20日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包