数据结构(哈夫曼树+KMP)之 数据加密+解密
生活随笔
收集整理的這篇文章主要介紹了
数据结构(哈夫曼树+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天代碼!!!
如果出存在什么問題,歡迎批評指正!謝謝!
總結
以上是生活随笔為你收集整理的数据结构(哈夫曼树+KMP)之 数据加密+解密的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 减肥期间喝酒会不会胖
- 下一篇: 植物粉末减肥可靠吗