c++實現哈夫曼樹,哈夫曼編碼,哈夫曼解碼(字符串去重,並統計頻率)


#include <iostream>
#include <iomanip>
#include <string>
#include <cstdlib>
using namespace std;

//定義哈夫曼樹存儲結構
typedef struct
{
    char data;    //存放結點數據
    int weight;      //記錄結點權值
    int parent, lchild, rchild;
}HTNode, * HuffmanTree;

//哈夫曼編碼存儲表示
typedef char** HuffmanCode;    //動態分配數組儲存哈夫曼編碼表

//初始化
HuffmanTree InitHuffman(HuffmanTree& HT, int n)
{
    if (n < 0)
    {
        cout << "輸入初態結點數目不正確!!!" << endl;
        return NULL;
    }
    else
    {
        int m = 2 * n - 1;            //n個葉子結點的哈夫曼樹有2n-1個結點
        HT = new HTNode[m + 1];        //0號單元未用,申請m+1個空間
        for (int i = 1; i <= m; i++)
        {
            HT[i].parent = 0;
            HT[i].lchild = 0;
            HT[i].rchild = 0;
            HT[i].weight = 0;
            HT[i].data = '-';
        }

        //輸入初始數據,為了方便理解HT[0]不存放數據
        cout << "請輸入初態結點數據及相應權值(格式形式:a 3):";
        for (int i = 1; i <= n; i++)
        {
            cin >> HT[i].data;
            cin >> HT[i].weight;
        }
        return HT;
    }
}

//打印HT
void PrintState(HuffmanTree& HT, int n)
{
    int m = 2 * n - 1;  //總結點數
    cout << "結點i\tdata值\tweight\tparent\tlchild\trchild" << endl;
    for (int i = 1; i <= m; i++)
    {
        cout << setw(3) << i << "\t";
        cout << setw(4) << HT[i].data << "\t";
        cout << setw(4) << HT[i].weight << "\t";
        cout << setw(4) << HT[i].parent << "\t";
        cout << setw(4) << HT[i].lchild << "\t";
        cout << setw(4) << HT[i].rchild << "\t" << endl;
    }
}

//選擇雙親域為0且兩權值最小的結點,選擇范圍為1到i-1
int* Select(HuffmanTree& HT, int n, int* Idx)
{
    int MIN, MinSecond;
    //打擂台法使權最小值和次權最小值最大(假設第一個值權值最小無法進行)
    MIN = MinSecond = 99999;
    //循環從1到n次
    for (int i = 1; i <= n; i++)
    {
        //如果雙親為0(未刪除結點與新生成結點),一定會執行if語句,尋找權最小值
        if ((HT[i].parent == 0) && HT[i].weight < MIN)
        {
            //將最小的權值給MinSecond方便尋找次最小值
            MinSecond = MIN;
            MIN = HT[i].weight;
            //記錄權最小值下標
            Idx[0] = i;
        }
        //否則如果滿足條件尋找次最小值
        else if ((HT[i].parent == 0) && HT[i].weight < MinSecond)
        {
            MinSecond = HT[i].weight;
            //記錄權次最小值下標
            Idx[1] = i;
        }

    }
    return Idx;
}

//構造哈夫曼樹
void CreateHuffmanTree(HuffmanTree& HT, int n)
{
    if (n <= 1)
        return;

    int m = 2 * n - 1;        //n個葉子結點的哈夫曼樹有2n-1個結點
    //從n+1開始構造哈弗曼樹
    for (int i = n + 1; i <= m; i++)
    {
        //Idx用於存放兩個返回最小權值的下標,i-1為前n個結點,后面隨着i增加,n也增加
        int* Idx = Idx = new int[2];
        Idx = Select(HT, i - 1, Idx);
        int s1 = Idx[0];   //權最小值下標
        int s2 = Idx[1];   //權次最小值下標
        //給兩權值最小的結點置雙親,使s1,s2結點不在錄入Select范圍
        HT[s1].parent = i;
        HT[s2].parent = i;

        //給新節點i左右孩子置位s1,s2
        HT[i].lchild = s1;
        HT[i].rchild = s2;

        //給新結點賦權值
        HT[i].weight = HT[s1].weight + HT[s2].weight;
        delete[]Idx;
    }
}

//哈夫曼編碼
void CreateHuffmanCode(HuffmanTree& HT, HuffmanCode& HC, int n)
{
    HC = new char* [n + 1];            //分配字符的空間
    char* TempCode = new char[n];    //分配臨時編碼表空間n個
    TempCode[n - 1] = '\0';            //編碼有葉子結點開始逆序存放,先將末尾結束符標志打上,為后序strcpy
    //for循環逐個逆序求葉子結點的哈夫曼編碼
    for (int i = 1; i <= n; i++)
    {
        int start = n - 1;            //開始存放的起點指向末尾,用於后面HC拷貝是的起始地址
        int NodeIdx = i;            //NodeIdx最開始存放葉子結點序號
        int ParentsIdx = HT[i].parent;    //Parents指向其雙親結點序號
        //若有雙親則由下到上進行編碼,編碼的字符,從序號1開始
        while (ParentsIdx)
        {
            start--;                //在一維數組末尾,strat先指向最后
            //若雙親結點的左結點序號為NodeIdx,將0打入一維臨時數組中,否則打入1
            if (HT[ParentsIdx].lchild == NodeIdx)
                TempCode[start] = '0';
            else
                TempCode[start] = '1';
            //結點的序號更新,進入上一層
            NodeIdx = ParentsIdx;
            ParentsIdx = HT[NodeIdx].parent;
        }
        //臨時一維數組存放編碼成功,開始用HC順序存放字符編碼
        HC[i] = new char[n - start];        //為序號為i的字符分配編碼空間
        strcpy_s(HC[i], n - start, &TempCode[start]);    //編碼字符串拷貝strcpy報錯因為沒有指定長度
    }
    //打印哈夫曼編碼
    cout << "---字符對應編碼----" << endl;
    for (int i = 1; i <= n; i++)
    {
        cout << HT[i].data << "\t\t" << HC[i] << endl;
    }
    delete []TempCode;
}

//輸入字符打印哈夫曼編碼
void PrintCode(HuffmanTree & HT, HuffmanCode & HC, int n)
{
    char *str =new char[100];    //存儲需要解碼的字符
    int flag = 0;                //flag用於檢查輸入是否錯誤
    cout << "請輸入需要編碼的字符:";
    cin >> str;
    //匹配字符並打印相應的哈夫曼編碼
    cout << "輸入的字符哈夫曼編碼為:";
    for (int j = 0; j < strlen(str); j++)
    {
        flag = 0;
        for (int i = 1; i <= n; i++)
        {
            //匹配成功打印編碼
            if (HT[i].data == str[j])
            {
                cout << HC[i] ;
                flag = 1;            //匹配成功
            }
        }
        //如果有匹配失敗的情況,則跳出循環
        if (flag == 0)
        {
            cout << "\n第" << j+1 << "字符輸入錯誤!" ;
            break;
        }
        
    }
    putchar(10);    
    delete []str;        //釋放str空間
}

//哈夫曼解碼並打印
void HuffmanDecode(HuffmanTree& HT, int n)
{
    char* str = new char[1024];
    cout << "請輸入二進制編碼:";
    cin >> str;

    int flag = 0;    //用於檢查二進制編碼是否輸入錯誤
    cout << "解碼對應字符為:";
    //遍歷二進制編碼
    for (int i = 0; i < strlen(str);)
    {
        int Root = 2 * n - 1;    //Root為根結點序號
        //當結點的左右孩子不為空時進入循環,由根結點進入
        while (HT[Root].lchild && HT[Root].rchild)
        {
            if (str[i] == '0')
                Root = HT[Root].lchild;
            else if (str[i] == '1')
                Root = HT[Root].rchild;
            else
            {
                cout << "輸入的二級制編碼有誤!" << endl;
                flag = 1;
                break;
            }
            i++;        //相后讀取二進制編碼,i值更新
        }
        //如果找到錯誤跳出循環
        if (flag)
            break;
        //打印編碼對應字符
        cout << HT[Root].data;
    }
    delete []str;
}

int main()
{
    int n;
    cout << "請輸入哈夫曼樹初態結點數:";
    cin >> n;

    //初始化哈夫曼樹
    HuffmanTree HT = InitHuffman(HT, n);
    //打印HT初態
    cout << "--------------------HT初態--------------------" << endl;
    PrintState(HT, n);
    //構造哈夫曼樹
    CreateHuffmanTree(HT, n);
    //打印HT終態
    cout << "--------------------HT終態--------------------" << endl;
    PrintState(HT, n);

    HuffmanCode HC;
    //哈夫曼編碼-由下而上
    CreateHuffmanCode(HT, HC, n);
    //打印字符對應編碼
    PrintCode(HT, HC, n);
    //哈夫曼解碼並打印-由上而下
    HuffmanDecode(HT, n);

    return 0;
}

//由於編譯器版本原因strcpy出現不安全原因,導致無法運行,后使用strcpy_s給予拷貝長度得到解決;把“==”寫成“=”導致報錯;
/*
輸入字符串統計字符個數(權值)
int CreateWeightArray(char* str, int* Array) {
//初始化權值數組,128為str[i]的最大數值
for (int i = 0; i < 128; i++)
{
Array[i] = 0;
}

int length = 0;

//利用下標記錄位權
for (int i = 0; str[i]; i++)
{
Array[str[i]]++; //值加1,下標即字符
}
//統計字符串去重后的長度
for (int i = 0; i < 128; i++)
{
if (Array[i] != 0)
{
length++;
}
}
return length;
}
*/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM