哈夫曼编码

首先实现一个最小堆(建立哈夫曼树需要用到)

贴代码

//最小堆.h
#include<iostream>
using namespace std;
class Node
{
public:
    int weigh;
    Node *left;
    Node *right;
    Node *parent;
};
typedef Node* Nodeptr;
class MinHeap
{
private:
    int size=10;
    int usage=0;  //heap[0]为哨兵
    Nodeptr* heap;
public:
    MinHeap(int size,Nodeptr a[],int usage)
    {
        //size = 10;  //测试用 用完删除!
        this->size = size;
        heap = new Nodeptr[size];
        heap[0] = new Node;
        for (int i = 1; i <= usage; i++)
        {
            heap[i] = a[i-1];
        }
        this->usage = usage;
        heap[0]->weigh = -1; // 建立最小堆 设为哨兵
        //调整为最小堆
        for (int i = usage / 2;i>0; i--)
        {
            Nodeptr tem = heap[i];
            int Parent, Child;
            for (Parent = i; Parent * 2 <= usage; Parent = Child) //判断有没有Parent(左)儿子
            {
                Child = Parent * 2; //假设左儿子更小
                if ((Child != usage) && (heap[Child]->weigh > heap[Child + 1]->weigh))
                    Child++;
                if (tem->weigh <= heap[Child]->weigh)
                    break;
                else
                    heap[Parent] = heap[Child];
            }
            heap[Parent] = tem;
        }
    }
    bool Add(Nodeptr e) //加入数据
    {
        if (IsFull())
        {
            cout << "+over flow" << endl;
            return false;
        }
        else
        {
            usage++;
            int i = usage;
            for (; heap[i / 2]->weigh > e->weigh; i = i / 2)
            {
                heap[i] = heap[i / 2];
            }
            heap[i] = e;
        }
        return true;
    }
    bool Delete(Nodeptr& e) //一定要小心,建立哈夫曼树的时候如果出错,看看这里的指针拷贝有没有出错
    {
        if (Isempty())
        {
            cout << "-over flow" << endl;
            return false;
        }
        e = heap[1];
        Nodeptr tem=heap[usage--];
        int Parent, Child;
        for (Parent = 1; Parent * 2 <= usage; Parent = Child) //判断有没有Parent(左)儿子
        {
            Child = Parent * 2; //假设左儿子更小
            if ((Child != usage) && (heap[Child]->weigh > heap[Child + 1]->weigh))
                Child++;
            if (tem->weigh <= heap[Child]->weigh)
                break;
            else
                heap[Parent] = heap[Child];
        }
        heap[Parent] = tem;
        return true;
    }
    bool IsFull()
    {
        return usage >= size-1;
    }
    bool Isempty()
    {
        return usage == 0;
    }

    //测试输出
    void pf()
    {
        for (int i = 0; i <= usage; i++)
        {
            cout << i << ":" << heap[i]->weigh << endl;
        }
    }

};

小顶堆写完后,哈夫曼树就很容易构建了,把每个元素看成一棵树,每次取堆顶元素,即选取树根权重最小的两棵树,合并为一棵树,树根权重为两棵树的树根权重之和,n个元素,进行n-1次就构建了一棵哈夫曼数啦。开心啊
下面贴代码

//哈夫曼树.h
#include"最小堆.h"
class HfTree
{
private:
    Nodeptr Head;
public:
    HfTree(Nodeptr a[], int size)
    {
        MinHeap m(size, a, size); //建立小顶堆
        Nodeptr t1, t2;
        for (int i = 1; i < size; i++)
        {
            Nodeptr tem = new Node;
            m.Delete(t1);
            m.Delete(t2);
            tem->left = t1;
            tem->right = t2;
            tem->weigh = t1->weigh + t2->weigh;
            t1->parent = tem;
            t2->parent = tem;
            m.Add(tem);
        }
        m.Delete(Head);
    }

    //单元测试
    void pf(Nodeptr a[], int size)
    {
        for (int i = 0; i < size; i++)
        {
            int length = 1;
            Nodeptr tem = a[i];
            while (tem->parent != Head)
            {
                length++;
                tem = tem->parent;
            }
            cout << a[i]->content << ":" << length << endl;
        }
    }
};

下面是主函数(仅做测试)

#include "哈夫曼树.h"
int main()
{
    /*小顶堆测试*/
//  Nodeptr a[10];
//  for (int i = 0; i < 7; i++)
//  {
//      a[i] = new Node;
//      a[i]->weigh = 7 - i;
//  }
//  MinHeap t(10,a,7);
//  t.pf();

    /*哈夫曼数测试*/
    Nodeptr a = new Node(2, 'a');
    Nodeptr b = new Node(3, 'b');
    Nodeptr c = new Node(4, 'c');
    Nodeptr d = new Node(5, 'd');
    Nodeptr e = new Node(6, 'e');
    Nodeptr f = new Node(7, 'f');
    Nodeptr arr[6];
    arr[0] = a;
    arr[1] = b;
    arr[2] = c;
    arr[3] = d;
    arr[4] = e;
    arr[5] = f;
    HfTree testtree(arr, 6);
    testtree.pf(arr, 6);
}

p.s 从文件里读取,进行压缩和解压缩还没完成,但程序主体大致完成了,周末有时间再写吧

这是分割线


贴代码:

#include<fstream>
#include<iostream>
#include <string>
#include<sstream>
using namespace std;
string readfile(int *a)
{
    cout << "请输入要压缩的文件:";
    string FilePath;
    cin >> FilePath;
    ifstream f(FilePath);
    while(!f)
    { 
        cout << "打开文件失败!" << endl;
        cout << "请输入要压缩的文件:";
        string FilePath;
        cin >> FilePath;
        f.open(FilePath);
    }
    string line;
    char tem;
    while (getline(f, line))
    {
//      istringstream s(line);
//      while (s >> tem)
//          a[tem]++;
        for (int i = 0; i < line.length(); i++)
        {
            tem = line[i];
            a[tem]++;
        }
        a['\n']++;
    }
//  cout << a['\n'] << endl;

//  char tem;
//  while (f>>tem)
//  {
//      //cout << tem << endl; //测试读入的字符
//      a[tem]++;
//  }
    f.close();
    return FilePath;
}

重写的主函数:

#include "哈夫曼树.h"
#include "readfile.h"

//传进来一个"10101001"的字符串,返回一个对应的ASCII字符
unsigned char StrToBin(string str)
{
    int a = atoi(str.c_str());
    int b = 1;
    int ans = 0;
    while (a != 0)
    {
        ans += a % 10 * b;
        b *= 2;
        a /= 10;
    }
    return (unsigned char)ans;
}

//把unsigned char类型转换为2进制字符串
string BinToStr(unsigned char c)
{
    string ans;
    while (c != 0)
    {
        ans.insert(ans.begin(), unsigned char(c % 2 + '0'));
        c /= 2;
    }

    if (ans.length() < 8)
    {
        ans.insert(ans.begin(), 8 - ans.length(), '0');
    }
    return ans;
}

/*解码*/
char decode(Nodeptr t,string &code)  //调试输出
{
    int i;
    for (i = 0; i < code.length(); i++)
    {
        if (t->content == 0)
        {
            if (t->left == nullptr&&t->right == nullptr)
            {
                code.erase(0, code.length());
                return '\n';
            }
            t = (code[i] - '0') ? t->right : t->left;
        }
        else
        {
            code.erase(0, i);
            //cout << t->content;
            return t->content;
        }
    }
}

int main()
{
    int num=0; //文本长度
    int HashTable[256] = { 0 };  //hash表 快速查找相应字符权重
    string FilePath = readfile(HashTable);//文件路径
    Nodeptr CharArr[256];  //存储结点数据 最多256个字符
    int size = 0; //存储文件中使用的字符个数
    for (int i = 0; i < 256; i++) //建立读取的文件字符构建节点数据
    {
        if (HashTable[i] != 0)
        {
            CharArr[size] = new Node;
            CharArr[size]->weigh = HashTable[i];
            CharArr[size]->content = char(i);
            size++;
        }
    }
    /*建立最小堆和哈夫曼树*/
    HfTree t(CharArr, size);
    string BinCode[256];  //存储二进制code
    t.setcode(BinCode, t.GTN());

    /*开始写入文件*/
    ifstream in(FilePath);  //读取文件
    ofstream out("out.myfile", ofstream::trunc);  //压缩文件
    /*首先写入哈夫曼数*/
    cout << size << endl;  //存储哈夫曼行数
    for (int i = 0; i < size; i++)
    {
        if (CharArr[i]->content == '\n')
        {
            cout << CharArr[i]->code << ' ' << "\\n" << endl;
        }
        else
            cout << CharArr[i]->code << ' ' << CharArr[i]->content << endl;
    }
    /**********************************************************************/
//  out << size << endl;  //存储哈夫曼行数
//  for (int i = 0; i < size; i++)
//  {
//      if (CharArr[i]->content == '\n')
//      {
//          out << CharArr[i]->code << ' ' << "\\n" << endl;
//      }
//      else
//      out << CharArr[i]->code << ' ' << CharArr[i]->content << endl;
//  }
    /**********************************************************************/
    char c;  //一次读入一个字符
    string temBuf;  //字符串缓存
    temBuf.clear();
    string line;
    while (getline(in, line))
    {
        for (int i = 0; i <= line.length(); i++)
        {
            if (i == line.length())
                c = '\n';
            else
                c = line[i];
            temBuf.append(BinCode[c]);
            if (temBuf.length() >= 8)
            {
                out << StrToBin(temBuf.substr(0, 8));
                temBuf.erase(0, 8);//删除前八个字符
            }
            num++;
        }
    }
    int appendZero = 0;     //附加0的数量
    if (!temBuf.empty())
    {
        appendZero = 8 - temBuf.length();
        temBuf.append(appendZero, '0');
        out << StrToBin(temBuf);
        temBuf.erase(0, 8);
    }
    in.close();
    out.close();
    /*完成文件的压缩*/
    /*解码*/
    cout << "请输入要解码的文件:";
    cin >> FilePath;
    in.open(FilePath, ifstream::binary);
    out.open("解压.txt", ofstream::trunc);
    temBuf.clear();
    //  in.read((char *)& c,sizeof(char));
    //  cout << c;
    //  cout<<BinToStr(c);
    //  while (1) //每次读取一个字符大小
    //  {
            //cout << c << endl;
    //      if (temBuf.length() < 8)
    //      {
    //          if (in.read((char*)& c, sizeof(char)))
    //              temBuf.append(BinToStr(c));  //转化为code形式
    //          else
    //          {
    //              do {
    //                  cout << decode(t.GTN(), temBuf);
    //              } while (!temBuf.empty());
    //              break;
    //          }
    //      }
    while (in.read((char*)& c, sizeof(char)))
    {
        temBuf.append(BinToStr(c));
    }
    int temnum = 0;  //临时计数变量
    while (!temBuf.empty())
    {
        temnum++;
        cout << decode(t.GTN(), temBuf);
    if(temnum==num)
        break;
    }
    getchar();
    getchar();
}

写的有点凌乱,有时间再整理一下吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值