数据结构(哈夫曼树+KMP)之 数据加密+解密

数据结构(哈夫曼树+KMP)之 数据加密+解密

原理:参考趣学数据结构

代码:

#include<stdio.h>
#include<stdlib.h>
#define N 100
#define INF 2^31-1
int next[N];
int Sum = 0;//权重总和
typedef struct fNode {//哈夫曼树中每个节点的信息int c;//字符int parent;//父节点,左右孩子,权重int lchild, rchild;int weight;
}fNode;
typedef struct rNode {//存储单个的编码字符的编码序列int r[N];int start;//有效编码起始的位置int length;
}rNode;
void huffMan(fNode fnode[], int n) {//构造哈夫曼树  选取二个最小的没有父节点的结点合并,以此类推for (int i = 0; i < n - 1; i++) {//n个字符n-1次的构造即可构造哈夫曼树int min1 = INF, min2 = INF;int u = -1, v = -1;for (int j = 0; j < n + i; j++) {//找二个最小的没有父节点的if (fnode[j].weight < min1 && fnode[j].parent == -1) {min2 = min1;//最值同时往前推v = u;min1 = fnode[j].weight;u = j;}else if (fnode[j].weight < min2 && fnode[j].parent == -1) {min2 = fnode[j].weight;v = j;}}fnode[n + i].weight = min1 + min2;fnode[n + i].lchild = u;fnode[n + i].rchild = v;fnode[u].parent = fnode[v].parent = n + i;//更新父节点}
}
void findHuffManCodePath(fNode fnode[], rNode rnode[], int n) {//寻找每个字符编码表示rNode temp;int start = n - 1;//最坏的哈夫曼树为一条链表for (int i = 0; i < n; i++) {start = n - 1;//每个字符的编码从叶子节点向根节点遍历int p = fnode[i].parent;int tempv = i;while (p != -1) {//p不等于-1表示有父节点if (tempv == fnode[p].lchild) {temp.r[start] = 0;}else {temp.r[start] = 1;}start--;tempv = p;p = fnode[p].parent;}for (int j = start + 1; j <= n - 1; j++) {//更新每个字符的编码数组rnode[i].r[j] = temp.r[j];rnode[i].start = start + 1;}rnode[i].length = n - start - 1;}printf("enCode:\n");int sum = 0;//遍历每个字符的编码数组for (int j = 0; j < n; j++) {//n个字符的编码遍历printf("%d的哈夫曼编码为:", fnode[j].c);for (int k = rnode[j].start; k <= n - 1; k++) {printf("%d", rnode[j].r[k]);}sum += (fnode[j].weight*rnode[j].length);printf("  ");}Sum = sum;printf("\n");printf("哈夫曼编码长度为:%d\n", sum);printf("\n");
}
void getNext(int *T, int *next, int m) {//求解当前字符前面的最大公共前缀和后缀int j = 1, k = 0;next[j] = 0;//从1开始计算while (j <= m) {if (k == 0 || T[k] == T[j]) {//从下标0开始计算++j;++k;//next[j] = k;if (T[k] == T[j]) {//改进的更新next数组的方法,减少不必要的回退next[j] = next[k];//没比较的可能}else {//也就是只有不相等的时候才有比较的可能next[j] = k;//与当前k位置的字符比较}}else {k = next[k];//回退查找前面的最大公共前缀和后缀}}/*printf("next数组值:");for (int i = 1; i <= m; i++) {printf("%d ", next[i]);}printf("\n");*/
}
int KMP(int * S, int* T, int pos, int n, int m) {//KMP算法进行模式匹配并替换掉解码的数据int i = pos, j = 1;while (i <= n && j <= m) {//不能在这里使用i<=n-m+1,否则可能会破坏(截断)匹配成功if (i > n - m + 1 && j == 1) {break;//再减少一点比较的次数}if (j == 0 || S[i] == T[j]) {i++;j++;}else {j = next[j];//根据最大公共前缀和后缀计算的next数组,j回退而i不回退}}//printf("\n---  %d ---\n", j);if (j == m + 1) {//返回查找成功子串的初始位置 ==不能写成=   写>更安全//printf("查找成功子串的初始位置为:%d\n", i);//return i - j;return i;}//printf("查找子串失败!\n");return -1;
}
/*   错误的逻辑方式
void deCode(int* enCodes, fNode* fnode, rNode* rnode,int n) {//对编码的字符进行解码for (int i = 0; i <n; i++) {int t = 1, tt = n - 1 - rnode[i].start +1;int temp[N];temp[0] = -1;for (int k = rnode[i].start; k <= n - 1; k++) {temp[t++]=rnode[i].r[k];printf("%d", temp[t-1]);}printf("\n");getNext(temp, next, tt);//计算next数组(最大公共前缀和后缀长度)printf("\n");int bools;bools = KMP(enCodes, temp, 1, Sum, tt, fnode[i].c);//模式匹配while (true) {if (bools == -1) {//break;}else {bools = KMP(enCodes, temp, bools, Sum, tt, fnode[i].c);}printf("\n");}}for (int i = 1; i <= Sum; i++) {if (enCodes[i] != -1) {//解码的字符遍历printf("%d", enCodes[i]);}}printf("\n");
}*/
void deCode(int* enCodes, fNode* fnode, rNode* rnode, int n) {//对编码的字符进行解码int ii=1;while(ii<=Sum) {//每前进一下for (int i = 0; i < n; i++) {//遍历查找所有的编码,即逐个地后移int t = 1, tt = n - 1 - rnode[i].start + 1;int temp[N];temp[0] = -1;for (int k = rnode[i].start; k <= n - 1; k++) {temp[t++] = rnode[i].r[k];//printf("%d", temp[t - 1]);}getNext(temp, next, tt);//计算next数组(最大公共前缀和后缀长度)int bools = KMP(enCodes, temp, ii, Sum, tt);//模式匹配if (bools != -1&&bools-ii==tt) {ii = bools;enCodes[ii - tt] =fnode[i].c;//解码for (int kk = ii - 1; kk > ii - tt; kk--) {enCodes[kk] = -1;//解码}/*for (int ik = 1; ik <= Sum; ik++) {if (enCodes[ik] != -1) {//解码的字符遍历printf("%d", enCodes[ik]);}}*/break;//查找成功,解码下一个字符}}}for (int i = 1; i <= Sum; i++) {if (enCodes[i] != -1) {//解码的字符遍历printf("%d", enCodes[i]);}}printf("\n");
}
int main() {printf("请输入要编码的数字:\n");fNode fnode[N];rNode rnode[N];int numbers[N];//原始数据int copyNumbers[N];//备份原始数据int enCodes[N];//字符编码int vNumber=1,indexs=0;while (vNumber != -1) {//输入,-1代表结束的输入的标志scanf_s("%d", &vNumber);if (vNumber != -1) {numbers[indexs] = vNumber;copyNumbers[indexs] = vNumber;indexs++;}}//统计字符数int copyIndex = 0,k=0;while (copyIndex < indexs) {int tempvv;if (copyNumbers[copyIndex] != -1) {tempvv = copyNumbers[copyIndex];//临时保存int counts = 0;for (int temIndex = copyIndex; temIndex < indexs; temIndex++) {if (tempvv == copyNumbers[temIndex]) {copyNumbers[temIndex] = -1;counts++;//统计字符数}}fnode[k].c = tempvv;fnode[k].weight = counts;k++;printf("%d:%d ", tempvv, counts);}copyIndex++;//向后移一步}printf("\n");int length = k;int u = 2 * length - 1;//哈夫曼总共有2n-1个结点for (int i = 0; i < u; i++) {//初始化结点的左右孩子和父节点信息fnode[i].lchild = -1;fnode[i].rchild = -1;fnode[i].parent = -1;}huffMan(fnode, length);findHuffManCodePath(fnode, rnode, length);printf("enSingleCode:\n");int starts = 1;enCodes[0] = -1;for (int startsi = 0; startsi < indexs; startsi++) {int tempValue = numbers[startsi];int tti;for (tti = 0; tti < length; tti++) {if (tempValue == fnode[tti].c) {break;//找到当前单个的字符对于的编码}}for (int ttti = rnode[tti].start; ttti <length ; ttti++) {//编码enCodes[starts++] = rnode[tti].r[ttti];printf("%d", rnode[tti].r[ttti]);}}printf("\ndeCode:\n");deCode(enCodes, fnode, rnode, length);//解码system("pause");return 0;
}

测试截图:

请添加图片描述

时间复杂度O(n x n x n),空间复杂度O(n) 辅助数组

彩蛋:
1.后期将更新为字符数组而不是整型数组,减少内存消耗,当然也可以是模板类型,有一部分decode注释的地方有问题,感兴趣的可以评论交流下,激发你们的思考,注释部分decode函数解密为死循环!!!
2.当然后期也将使用文件操作读文件加密和解密!
3.因为不同的编码方式不同,大家可以改进,不要每次都左树为0,右树为1,这样很容易被别人解密,可以适当地调整不同层的子树根节点的左右01编码加密方式,当然也可以是其他数字或字符作为左右加密符号!!!

打印调试了2天代码!!!

如果出存在什么问题,欢迎批评指正!谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/311768.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

扩展性思维

扩展性思维&#xff0c;简单来说就是举一反三、触类旁通&#xff1b;它的核心目标是提升我们思维的广度&#xff0c;也就是让我们的知识树变得更加开阔&#xff1b;具备了这样的知识树后才能发现要解决的同类型事情一起解决。下面以几个举一反三的例子讲一下扩展性思维&#xf…

C语言 ---文件读取

C语言 —文件读取 参考学习链接&#xff1a; https://blog.csdn.net/qq_38149046/article/details/80359743 http://c.biancheng.net/view/2068.html 代码&#xff1a; #include<stdio.h> #include<stdlib.h> int main() {FILE *fp;char ch;//如果文件不存在&am…

个人永久性免费-Excel催化剂功能第119波-一大波虚构数据,支持多国语言版本

日常的数据分析过程中或者制作教程过程中&#xff0c;难免要生成一些示例数据、虚构数据&#xff0c;Excel除了几个基础的随机数值函数外&#xff0c;没有什么额外的支持&#xff0c;本篇再次补全这个缺陷。虚构函数的由来本次的功能&#xff0c;使用老外做的轮子&#xff0c;有…

python可变参数和关键字参数位置_python中函数的默认参数和可变长参数如何排列?...

参数在python中总是通过赋值进行传递的。在默认情况下&#xff0c;参数是通过其位置进行匹配的&#xff0c;从左到右&#xff0c;而且必须精确的传递和函数头部参数名一样多的参数。 这种默认的传递方式很简单 def f(a,b,c): print(a,b,c) f(1,2,3) 1 2 3 python中可以使用基于…

word List 24

word List 24 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

[USACO1.5]八皇后 Checker Challenge

给定一个 NN 的棋盘&#xff0c;请你在上面放置 N 个棋子&#xff0c;要求满足&#xff1a; 每行每列都恰好有一个棋子 每条对角线上都最多只能有一个棋子 上图给出了当 N6 时的一种解决方案&#xff0c;该方案可用序列 2 4 6 1 3 5 来描述&#xff0c;该序列按顺序给出了从…

python窗口显示表格_pyqt 调用tablewidget窗口时无法显示窗口内容

请教一下大家&#xff0c;想实现在主窗口中点击按钮调用一个新窗口&#xff0c;这个窗口是tablewidget&#xff0c;目前可以实现弹出窗口&#xff0c;但是无法显示表格&#xff0c;只有一个空白窗口。实在找不到原因了&#xff0c;还请大家指点。 这是主窗口代码 # -*- coding:…

程序员过关斩将--redis做消息队列,香吗?

菜菜哥&#xff0c;我刚做完了一个订单系统&#xff0c;感觉很简单呀说说看&#xff0c;大量的订单状态怎么处理的&#xff1f;我设计的时候可是考虑了这一点&#xff0c;所以用了异步处理&#xff0c;采用了MQ那用的什么MQ呢&#xff0c;透露一下呗我用的redis做的MQ&#xff…

word List25

word List25 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

c++ 多核cpu序列号_关于 CPU 的一些基本知识总结

优质文章&#xff0c;及时送达作者 | 骏马金龙链接 | cnblogs.com/f-ck-need-u/p/11141636.htm关于CPU和程序的执行CPU是计算机的大脑。1、程序的运行过程&#xff0c;实际上是程序涉及到的、未涉及到的一大堆的指令的执行过程。当程序要执行的部分被装载到内存后&#xff0c;C…

.NET Core开发实战(第4课:Startup:掌握ASP.NET Core的启动过程)--学习笔记

04 | Startup&#xff1a;掌握ASP.NET Core的启动过程新建一个 ASP.NET Core Web 应用程序选择 APIpublic class Program {public static void Main(string[] args){CreateHostBuilder(args).Build().Run();}public static IHostBuilder CreateHostBuilder(string[] args) >…

C语言---二进制和文本文件的备份

C语言—二进制和文本文件的备份 学习参考&#xff1a; https://www.cnblogs.com/jackytang/p/9011127.html 代码&#xff1a; #include <stdio.h> #include <stdlib.h> int copyFile(char *fileRead, char *fileWrite); int main() {char fileRead[100]; // 要…

聊聊统一身份认证服务

源宝导读&#xff1a;当企业的应用系统逐渐增多后&#xff0c;每个系统单独管理各自的用户数据容易行成信息孤岛&#xff0c;分散的用户管理模式阻碍了企业应用向平台化演进。本文将介绍构建统一身份认证的技术方案和思路。一、背景当企业的应用系统逐渐增多后&#xff0c;每个…

Word List 26

Word List 26 如果存在什么问题&#xff0c;欢迎批评指正!谢谢&#xff01;

[蓝桥杯2015决赛]五星填数-枚举+数论

解题思路&#xff1a; 对于一种符合题意的情况&#xff0c;旋转和对称之后结果也是一样&#xff0c;所以我们要除去旋转(/5)和对称(/2)的情况&#xff0c;所以结果要除以10 代码如下&#xff1a; #include <iostream> #include <algorithm> using namespace std;…

word List27

word List27 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

如何看待程序媛们的职场焦虑和未来职业规划?

本文公众号平台首发于【DotNET技术圈】&#xff0c;来自邹溪源一 引子昨天&#xff0c;有一位通过我们长沙.NET技术社区公众号【DotNET技术圈】加我的好友的姑娘问了一个关于职业规划的问题&#xff1a;“在你的身边有没有认识的女程序员&#xff0c;可以告诉一下我们她们的职业…

[蓝桥杯2016初赛]搭积木-枚举,next_permutation

代码如下&#xff1a; #include <iostream> #include <algorithm> using namespace std;int a[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};bool check() {if (a[0] > a[1] && a[0] > a[2] &&a[1] > a[3] && a[1] > a[4] &&a…

word List28

word List28 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

target not created怎么解决_怎么才能最短时、高效、踏实地学习 Python(附链接)...

作者&#xff1a;九章算法来源&#xff1a;数据Seminar本文约2400字&#xff0c;建议阅读5分钟本文教你高效学习全球主流编程语言Python。标签&#xff1a;PythonPython是当前全球的主流编程语言之一&#xff0c;基于其简洁的语法结构&#xff0c;可以让开发者用更少的代码完成…