C++数据结构之文件压缩(哈夫曼树)实例详解

C++数据结构之文件压缩(哈夫曼树)实例详解

概要:

项目简介:利用哈夫曼编码的方式对文件进行压缩,并且对压缩文件可以解压

开发环境:windows vs2013

项目概述:

        1.压缩

a.读取文件,将每个字符,该字符出现的次数和权值构成哈夫曼树

b.哈夫曼树是利用小堆构成,字符出现次数少的节点指针存在堆顶,出现次数多的在堆底

c.每次取堆顶的两个数,再将两个数相加进堆,直到堆被取完,这时哈夫曼树也建成

d.从哈夫曼树中获取哈夫曼编码,然后再根据整个字符数组来获取出现了得字符的编码

e.获取编码后每次凑满8位就将编码串写入到压缩文件(value处理编码1与它即可,0只移动位)

f.写好配置文件,统计每个字符及其出现次数,并以“字符+','+次数”的形式保存到配置文件中

      2.解压

a.读取配置文件,统计所有字符的个数

b.构建哈夫曼树,读解压缩文件,将所读到的编码字符的这个节点所所含的字符写入到解压缩文件中,知道将压缩文件读完

c.压缩解压缩完全完成,进行小文件大文件的测试

实例代码:

#pragma once
#include<vector> 

template<class T>
struct Less
{
  bool operator()(const T& l, const T& r) const
  {
    return l < r;
  }
}; 

template<class T>
struct Greater
{
  bool operator()(const T& l, const T& r) const
  {
    return l > r;
  }
}; 

template<class T, class Compare>
class Heap
{
public:
  Heap()
  {} 

  Heap(T* array, size_t n)   //建堆
  {
    _array.reserve(n); 

    for (size_t i = 0; i < n; i++)
    {
      _array.push_back(array[i]);
    } 

    for (int i = (_array.size() - 2) >> 1; i >= 0; --i)
    {
      _AdjustDown(i);
    }
  } 

  const T& Top()const
  {
    return _array[0];
  } 

  void Push(const T& x)
  {
    _array.push_back(x);
    _AdjustUp(_array.size() - 1);
  } 

  size_t Size()
  {
    return _array.size();
  } 

  void Pop()
  {
    assert(_array.size() > 0);
    swap(_array[0], _array[_array.size() - 1]);
    _array.pop_back();
    _AdjustDown(0);
  } 

  bool Empty()
  {
    return _array.size() == 0;
  } 

  void Print()
  {
    for (size_t i = 0; i < _array.size(); ++i)
    {
      cout << _array[i] << " ";
    }
    cout << endl;
  } 

protected:
  void _AdjustUp(int child)  //上调
  {
    Compare ComFunc;
    int parent = (child - 1) >> 1;
    while (child)
    {
      if (ComFunc(_array[child], _array[parent]))
      {
        swap(_array[child], _array[parent]);
        child = parent;
        parent = (child - 1) >> 1;
      }
      else
      {
        break;
      }
    }
  } 

  void _AdjustDown(int root)   //下调
  {
    Compare ComFunc;
    int parent = root;
    int child = root * 2 + 1;
    while (child < _array.size())
    {
      if (child + 1 < _array.size() && ComFunc(_array[child + 1], _array[child]))
      {
        ++child;
      } 

      if (ComFunc(_array[child], _array[parent]))
      {
        swap(_array[child], _array[parent]);
        parent = child;
        child = parent * 2 + 1;
      }
      else
      {
        break;
      }
    }
  } 

protected:
  vector<T> _array; 

}; 

void TestHeap()
{
  int a[] = { 10, 11, 13, 12, 16, 18, 15, 17, 14, 19 };
  //Heap<int> heap(a, sizeof(a) / sizeof(a[0]));
  //Heap<int, Less<int>> heap(a, sizeof(a) / sizeof(a[0]));
  Heap<int, Greater<int>> heap(a, sizeof(a) / sizeof(a[0]));
  heap.Print();
  heap.Push(25);
  heap.Print();
  heap.Pop();
  heap.Print();
}

#pragma once
#include"Heap.h" 

template<class T>
struct HuffmanTreeNode
{
  HuffmanTreeNode<T>* _left;
  HuffmanTreeNode<T>* _right;
  HuffmanTreeNode<T>* _parent;
  T _w;       //权值 

  HuffmanTreeNode(const T& x)
    :_w(x)
    , _left(NULL)
    , _right(NULL)
    , _parent(NULL)
  {} 

}; 

template<class T>
class HuffmanTree
{
  typedef HuffmanTreeNode<T> Node;
public:
  HuffmanTree()
    :_root(NULL)
  {} 

  HuffmanTree(T* a, size_t n, const T& invalid = T())  //构建哈夫曼树
  {
    struct Compare
    {
      bool operator()(Node* l, Node* r) const
      {
        return l->_w < r->_w;
      }
    }; 

    Heap<Node*, Compare> minHeap;
    for (size_t i = 0; i < n; ++i)
    {
      if (a[i] != invalid)
      {
        minHeap.Push(new Node(a[i]));
      }
    } 

    while (minHeap.Size() > 1)
    {
      Node* left = minHeap.Top();
      minHeap.Pop();
      Node* right = minHeap.Top();
      minHeap.Pop();
      Node* parent = new Node(left->_w + right->_w);
      parent->_left = left;
      parent->_right = right;
      left->_parent = parent;
      right->_parent = parent;
      minHeap.Push(parent);
    }
    _root = minHeap.Top();
  } 

  Node* GetRoot()
  {
    return _root;
  } 

  ~HuffmanTree()
  {
    _Destory(_root);
  } 

protected:
  void _Destory(Node* root)
  {
    if (root == NULL)
    {
      return;
    } 

    _Destory(root->_left);
    _Destory(root->_right);
    delete root;
  } 

  HuffmanTree(const HuffmanTree<T>& tree);
  HuffmanTree& operator=(const HuffmanTree<T>& tree); 

protected:
  Node* _root; 

}; 

void TestHuffmanTree()
{<pre code_snippet_id="2340790" snippet_file_name="blog_20170418_2_3778260" name="code" class="cpp">#pragma once 

#include<iostream>
using namespace std;
#include<assert.h>
#include"HuffmanTree.h" 

typedef long long LongType;
struct CharInfo
{
  unsigned char _ch;     //字符
  LongType _count;     //字符出现的次数
  string _code;      //huffman编码 

  CharInfo(LongType count = 0)
    :_count(count)
    , _ch(0)
    , _code("")
  {} 

  bool operator<(const CharInfo& info) const
  {
    return _count < info._count;
  } 

  CharInfo operator+(const CharInfo& info)
  {
    return CharInfo(_count + info._count);
  } 

  bool operator!=(const CharInfo& info)const
  {
    return _count != info._count;
  }
}; 

struct CountInfo
{
  unsigned char _ch;    //字符
  LongType _count;     //字符出现的次数
}; 

class FileCompress
{
public:
  FileCompress()
  {
    for (size_t i = 0; i < 256; ++i)
    {
      _info[i]._ch = i;
    }
  } 

  void Compress(const char* filename)
  {
    assert(filename); 

    //统计字符出现的次数,均已二进制方式读写
    FILE* fout = fopen(filename, "rb");
    assert(fout);
    int ch = fgetc(fout); 

    string CompressFile = filename;
    CompressFile += ".huffman";
    FILE* fin = fopen(CompressFile.c_str(), "wb");
    assert(fin);
    CountInfo info; 

    while (ch != EOF)
    {
      _info[(unsigned char)ch]._count++;
      ch = fgetc(fout);
    } 

    //构建huffman tree
    CharInfo invalid;
    invalid._count = 0;
    HuffmanTree<CharInfo> tree(_info, 256, invalid); 

    //生成huffman code
    GetHuffmanCode(tree.GetRoot()); 

    //压缩
    //写配置文件
    for (size_t i = 0; i < 256; ++i)
    {
      if (_info[i]._count)
      {
        info._ch = _info[i]._ch;
        info._count = _info[i]._count;
        fwrite(&info, sizeof(info), 1, fin);
      }
    } 

    info._count = -1;
    fwrite(&info, sizeof(info), 1, fin); 

    fseek(fout, 0, SEEK_SET);   //返回到文件开始
    ch = fgetc(fout);
    char value = 0;     //二进制
    int pos = 0;    //左移位数
    while (ch != EOF)
    {
      string& code = _info[(unsigned char)ch]._code;
      size_t i = 0;
      for (i = 0; i < code.size(); ++i)
      {
        value <<= 1;
        ++pos;
        if (code[i] == '1')
        {
          value |= 1;
        }
        if (pos == 8)       //满8位写进文件中
        {
          fputc(value, fin);
          value = 0;
          pos = 0;
        }
      }
      ch = fgetc(fout);
    }
    if (pos)
    {
      value <<= (8 - pos);
      fputc(value, fin);
    }
    fclose(fin);
    fclose(fout);
  } 

  void GetHuffmanCode(HuffmanTreeNode<CharInfo>* root)    //huffman编码
  {
    if (root == NULL)
    {
      return;
    } 

    if (root->_left == NULL && root->_right == NULL)
    {
      HuffmanTreeNode<CharInfo> *parent, *cur;
      cur = root;
      parent = cur->_parent;
      string& code = _info[(unsigned char)root->_w._ch]._code;
      while (parent)
      {
        if (cur == parent->_left)
        {
          code += '0';
        }
        else
        {
          code += '1';
        }
        cur = parent;
        parent = cur->_parent;
      }
      reverse(code.begin(), code.end());
    }
    GetHuffmanCode(root->_left);
    GetHuffmanCode(root->_right);
  } 

  //解压
  void UnCompress(const char* filename)
  {
    assert(filename);
    string UnCompressFile = filename;
    size_t index = UnCompressFile.rfind('.');
    assert(index != string::npos);
    UnCompressFile = UnCompressFile.substr(0, index);
    UnCompressFile += ".unhuffman";
    FILE* fout = fopen(filename, "rb");
    assert(fout);
    FILE* fin = fopen(UnCompressFile.c_str(), "wb");
    assert(fin);
    CountInfo info;
    fread(&info, sizeof(CountInfo), 1, fout); 

    //读配置信息
    while (1)
    {
      fread(&info, sizeof(CountInfo), 1, fout);
      if (info._count == -1)
      {
        break;
      }
      _info[(unsigned char)info._ch]._ch = info._ch;
      _info[(unsigned char)info._ch]._count = info._count;
    } 

    //重建huffman树
    CharInfo invalid;
    invalid._count = 0;
    HuffmanTree<CharInfo> tree(_info, 256, invalid);
    HuffmanTreeNode<CharInfo>* root = tree.GetRoot();
    HuffmanTreeNode<CharInfo>* cur = root;
    LongType count = root->_w._count;
    int value = fgetc(fout); 

    if (value == EOF)       //只有一种字符
    {
      if (info._ch != 0)
      {
        while (cur->_w._count--)
        {
          fputc(cur->_w._ch, fin);
        }
      }
    }
    else
    {
      while (value != EOF)
      {
        int pos = 7;
        char test = 1;
        while (pos >= 0)
        {
          if (value & (test << pos))
          {
            cur = cur->_right;
          }
          else
          {
            cur = cur->_left;
          }
          if (cur->_left == NULL && cur->_right == NULL)
          {
            fputc(cur->_w._ch, fin);
            cur = root;
            if (--count == 0)
            {
              break;
            }
          }
          --pos;
        }
        value = fgetc(fout);
      }
    } 

    fclose(fout);
    fclose(fin);
  } 

protected:
  CharInfo _info[256];   //所有字符信息
}; 

void TestFileCompress()
{
  FileCompress fc;
  //fc.Compress("实验.doc");
  //fc.UnCompress("实验.doc.huffman");
  //fc.Compress("qq.JPG");
  //fc.UnCompress("qq.JPG.huffman");
  //fc.Compress("www.MP3");
  //fc.UnCompress("www.MP3.huffman"); 

  fc.Compress("yppppp.txt");
  fc.UnCompress("yppppp.txt.huffman");
}</pre><br>
int array[10] = { 2, 4, 6, 9, 7, 8, 5, 0, 1, 3 };HuffmanTree<int> t(array, 10);}
<pre></pre>
<p></p>
<pre></pre>
<p></p>
<p></p>
<p></p>
<pre code_snippet_id="2340790" snippet_file_name="blog_20170418_3_1128302" name="code" class="cpp">#include <iostream>
#include <assert.h>
#include <windows.h>
using namespace std; 

#include"Heap.h"
#include"HuffmanTree.h"
#include"FileCompress.h" 

int main()
{
  //TestHeap();
  TestHuffmanTree();
  TestFileCompress(); 

  system("pause");
  return 0;
}</pre><br>
<br>
<p></p>
<p><br>
</p>
<p><br>
</p>
<link rel="stylesheet" href="http://static.blog.csdn.net/public/res-min/markdown_views.css?v=1.0" rel="external nofollow" >

以上就是哈夫曼树的实例详解,如有疑问请留言或者到本站社区交流,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • C语言数据结构 快速排序实例详解

    C语言数据结构 快速排序实例详解 一.快速排序简介 快速排序采用分治的思想,第一趟先将一串数字分为两部分,第一部分的数值都比第二部分要小,然后按照这种方法,依次对两边的数据进行排序. 二.代码实现 #include <stdio.h> /* 将两个数据交换 */ void swap(int* Ina , int* Inb) { int temp = *Ina; *Ina = *Inb; *Inb = temp; } /* 进行一趟的快速排序,把一个序列分为两个部分 */ int getPart

  • C语言数据结构之线索二叉树及其遍历

    C语言数据结构之线索二叉树及其遍历 遍历二叉树就是以一定的规则将二叉树中的节点排列成一个线性序列,从而得到二叉树节点的各种遍历序列,其实质是:对一个非线性的结构进行线性化.使得在这个访问序列中每一个节点都有一个直接前驱和直接后继.传统的链式结构只能体现一种父子关系,¥不能直接得到节点在遍历中的前驱和后继¥,而我们知道二叉链表表示的二叉树中有大量的空指针,当使用这些空的指针存放指向节点的前驱和后继的指针时,则可以更加方便的运用二叉树的某些操作.引入线索二叉树的目的是: 为了加快查找节点的前驱和后继

  • C语言数据结构旋转链表的实现

    C语言数据结构旋转链表的实现 实例: 给出链表1->2->3->4->5->null和k=2 返回4->5->1->2->3->null 分析: 感觉很直观,直接把分割点找出来就行,记得k可能大于len,要取模 代码: /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x

  • C++数据结构与算法之反转链表的方法详解

    本文实例讲述了C++数据结构与算法之反转链表的方法.分享给大家供大家参考,具体如下: 算法概述:要求实现将一条单向链表反转并考虑时间复杂度. 算法分析: 数组法(略): 将列表元素逐个保存进数组,之后再逆向重建列表 点评:实现逻辑最简单,需要额外的内存开销. 移动指针: 通过三个指针逐个从链表头开始逐一反转链表元素的指针 点评:不需要额外的内存开销,会改变原始链表. 递归: 以递归的方式首先找到链表尾部,再逐一反转指针 点评:不需要额外的内存开销,不会改变原始链表. 算法实现: 构建链表结构 /

  • C++ 数据结构之对称矩阵及稀疏矩阵的压缩存储

    对称矩阵及稀疏矩阵的压缩存储 1.稀疏矩阵 对于那些零元素数目远远多于非零元素数目,并且非零元素的分布没有规律的矩阵称为稀疏矩阵(sparse). 人们无法给出稀疏矩阵的确切定义,一般都只是凭个人的直觉来理解这个概念,即矩阵中非零元素的个数远远小于矩阵元素的总数,并且非零元素没有分布规律. 实现代码: //稀疏矩阵及其压缩存储 #pragma once #include <vector> #include <iostream> using namespace std; templa

  • C语言数据结构之顺序数组的实现

    C语言数据结构之顺序数组的实现 以下为展示顺序数组的示例: 1.用C语言实现的版本 #include<stdio.h> /* EOF(=^Z或F6),NULL */ #include<math.h> /* floor(),ceil(),abs() */ #include<stdlib.h> /*申请和释放内存*/ #include<stdarg.h> /*可变参数*/ #define OK 1 //成功标志 #define ERROR 0 //错误标志 #d

  • C++数据结构与算法之双缓存队列实现方法详解

    本文实例讲述了C++数据结构与算法之双缓存队列实现方法.分享给大家供大家参考,具体如下: "双缓存队列"是我在一次开发任务中针对特殊场景设计出来的结构.使用场景为:发送端持续向接收端发送数据包--并且不理会接收端是否完成业务逻辑.由于接收端在任何情况下停止响应即可能产生数据丢失,因此无法简单的设计一条线程安全队列来对数据写入或读取(读取数据时将队列上锁视为对写入的停止响应). 鉴于此,我的设计思路如下: 接收端首先向A队列中写入数据,然后当数据处理请求到来的时候切换到B队列继续写入,之

  • ES6新增数据结构WeakSet的用法详解

    WeakSet和Set类似,同样是元素不重复的集合,它们的区别是WeakSet内的元素必须是对象,不能是其它类型. 特性: 1.元素必须是对象. 添加一个number类型的元素. const ws = new WeakSet() ws.add(1) 结果是报类型错误. TypeError: Invalid value used in weak set 添加一个对象. const ws = new WeakSet() var a = {p1:'1', p2:'2'} ws.add(a) conso

  • C++数据结构与算法之哈夫曼树的实现方法

    本文实例讲述了C++数据结构与算法之哈夫曼树的实现方法.分享给大家供大家参考,具体如下: 哈夫曼树又称最优二叉树,是一类带权路径长度最短的树. 对于最优二叉树,权值越大的结点越接近树的根结点,权值越小的结点越远离树的根结点. 前面一篇图文详解JAVA实现哈夫曼树对哈夫曼树的原理与java实现方法做了较为详尽的描述,这里再来看看C++实现方法. 具体代码如下: #include <iostream> using namespace std; #if !defined(_HUFFMANTREE_H

  • java 流操作对文件的分割和合并的实例详解

    java 流操作对文件的分割和合并的实例详解 学习文件的输入输出流,自己做一个小的示例,对文件进行分割和合并. 下面是代码: package com.dufy.file; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.SequenceInputStream; import java.ut

  • python文件特定行插入和替换实例详解

    python文件特定行插入和替换实例详解 python提供了read,write,但和很多语言类似似乎没有提供insert.当然真要提供的话,肯定是可以实现的,但可能引入insert会带来很多其他问题,比如在插入过程中crash掉可能会导致后面的内容没来得及写回. 不过用fileinput可以简单实现在特定行插入的需求: Python代码 import os import fileinput def file_insert(fname,linenos=[],strings=[]): ""

  • Python读取文件的四种方式的实例详解

    目录 学生数量特别少的情况 停车场空间不够时怎么办? 怎么加快执行效率? 怎么加快处理速度? 结语 故事背景:最近在处理Wikipedia的数据时发现由于数据量过大,之前的文件读取和数据处理方法几乎不可用,或耗时非常久.今天学校安排统一核酸检查,刚好和文件读取的过程非常相似.正好借此机会和大家一起从头梳理一下几种文件读取方法. 故事设定:现在学校要求对所有同学进行核酸采集,每位同学先在宿舍内等候防护人员(以下简称“大白”)叫号,叫到自己时去停车场排队等候大白对自己进行采集,采集完之后的样本由大白

  • java哈夫曼树实例代码

    本文实例为大家分享了哈夫曼树java代码,供大家参考,具体内容如下 package boom; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Queue; class Node<T> implements Comparable<Node<T>>{ private T

  • C#求解哈夫曼树,实例代码

    复制代码 代码如下: class HuffmanTree    {        private Node[] data;        public int LeafNum { get; set; }        public Node this[int index]        {            get { return data[index]; }            set { data[index] = value; }        }        public Hu

  • Python数据结构与算法之链表定义与用法实例详解【单链表、循环链表】

    本文实例讲述了Python数据结构与算法之链表定义与用法.分享给大家供大家参考,具体如下: 本文将为大家讲解: (1)从链表节点的定义开始,以类的方式,面向对象的思想进行链表的设计 (2)链表类插入和删除等成员函数实现时需要考虑的边界条件, prepend(头部插入).pop(头部删除).append(尾部插入).pop_last(尾部删除) 2.1 插入: 空链表 链表长度为1 插入到末尾 2.2 删除 空链表 链表长度为1 删除末尾元素 (3)从单链表到单链表的一众变体: 带尾节点的单链表

  • Python3.5文件读与写操作经典实例详解

    本文实例讲述了Python3.5文件读与写操作.分享给大家供大家参考,具体如下: 1.文件操作的基本流程: (1)打开文件,得到文件句柄并赋值给一个变量 (2)通过句柄对文件进行操作 (3)关闭文件 2.基本操作举例: 现有一个命名为song.txt的文件,里面存放最喜爱的英文歌,内容如下: take me to your heart hiding from the rain and snow 藏身于雨雪之中 trying to forget but i won't let go 努力忘记,但我

  • JS中的算法与数据结构之二叉查找树(Binary Sort Tree)实例详解

    本文实例讲述了JS中的算法与数据结构之二叉查找树(Binary Sort Tree).分享给大家供大家参考,具体如下: 二叉查找树(Binary Sort Tree) 我们之前所学到的列表,栈等都是一种线性的数据结构,今天我们将学习计算机中经常用到的一种非线性的数据结构--树(Tree),由于其存储的所有元素之间具有明显的层次特性,因此常被用来存储具有层级关系的数据,比如文件系统中的文件:也会被用来存储有序列表等. 在树结构中,每一个结点只有一个前件,称为父结点,没有前件的结点只有一个,称为树的

  • node.js express框架实现文件上传与下载功能实例详解

    本文实例讲述了node.js express框架实现文件上传与下载功能.分享给大家供大家参考,具体如下: 背景 昨天吉视传媒的客户对IPS信息发布系统又提了一个新需求,就是发布端发送消息时需要支持附件的上传,而接收端可以对发布端上传的附件进行下载:接收端回复消息时也需要支持上传附件,发布端可以对所有接收端上传的附件进行打包下载. 功能实现 前台部分 前台使用webUploader插件即可,这是百度开发的一款文件上传组件,具体使用查看它的API即可.这个项目之前开发的时候前台使用了angular.

随机推荐