哈夫曼树

这个程序设计了5个类,分别是

队列结点类( class QueueNode ),因为该队列将来会用来存储树的结点 ,因此将队列设计成模板(template   ),可以存储任何自定义的数据类型,用队列输入输出时只需要对输入输出进行重载 就能输出结点里的数据。

classQueueNode          //自定义结点类型(队列结点)

{

private:

T data;

QueueNode *next;

public:

void setData(T data) {     

   this->data = data;

}                   

void setNext(QueueNode* p) {   

   this->next = p;

}

T& getData() {             

   return this->data;

}

QueueNode*& getNext() 

{

   return this->next;

}

};

该结点就是树的结点类型,由于队列结点是存储数据 ,因此可以设计一个队列类,用来实现队列的一些基本功能 

队列类( class

priorityQueue ),基本的的操作也就是出队和入队 ,在入队这一块,由于将来的哈夫曼树的构造需要一直从结点中找到权值最小的结点,而队列的出队是只能从最顶端出队,因此入队的时候需要对结点排序 ,权值最小的放到最上层,出队时直接拿来用就行。

classpriorityQueue 

{

public:

priorityQueue();          //构造函数初始化数据   

void insertQueue(T e);    //拥有入队排序的功能

T pop();                  //出队,出队的同时删除队列的顶端结点

int getNumOfQueue() {    

   return num;            //返回结点数量

}

void print() {              //打印显示   

    QueueNode *p;

    p = this->front;        // p指向头节点

    if (this->front == this->rear)      //先判断队列是否为空

    {

       cout << "当前队列为NULL" << endl;

       return;

    }

    cout << "当前队列的数据为:" << endl;

    while (p != this->rear)

    {

       cout <getNext()->getData() << "\t"<<(p->getNext()->getData()).getWeight()<< endl;

       p = p->getNext();

    }

    cout << endl;

}

protected:

QueueNode* front;     //顶端指针存储最顶端结点的地址  

QueueNode* rear;      //底端指针存储最低端结点的地址

int num;                 //结点的数量

};

其中所包含的几种算法如下:

priorityQueue::priorityQueue()      //实现优先排序

{

num = 0;                           //初始化结点的数量为0

front = rear = new QueueNode;   //类似于链表的头节点,其地址初始由顶端和低端指针存储

rear->setNext(NULL);               //头结点的next指针指向为NULL

}


voidpriorityQueue::insertQueue(T e)    //入队排序函数

{

num++;                   //结点的数量增加

QueueNode *p = new QueueNode,*temp = front->getNext(), *pre = front;

//p为新结点申请存储空间   temp和pre分别是两个连续的指针,即pre的next存放的地点永远有temp接收 

p->setData(e);         //往新结点里存储数据


/*如果队列为空不进行循环,将新结点直接插入头结点的后面

如果队列不为NULL,但是新设置的结点数据较小需要插入到队列里需要进行while循环*/

while (temp && temp->getData()

    pre = temp;              //pre在前所以将来p(新结点)将插入在其后面

    temp = temp->getNext();  //temp在后所以将来p(新结点)将插入在其前面

}

p->setNext(temp);           //新结点的next存放temp,即实现了p(新结点)插入temp之前

pre->setNext(p);


while (temp) {               //循环到最低端的结点

    pre = temp;

    temp = temp->getNext();

}

this->rear = pre;            //最低端存储指针存放最低端的结点地址

}


TpriorityQueue::pop()        //出队

{

num--;                      //结点的数量少1

T e = front->getNext()->getData();

QueueNode* p = front->getNext();     //找到队列顶端结点

front->setNext(p->getNext());          //顶端指针下移

delete p;       //释放空间原先的顶端结点


return e;

}

其次就是哈夫曼树的类 :

树的结点类(class HfmNode ),和队列一样,需要先设计一个树的结点类,用于存储数据 ,在这包括树的左右孩子指针,和储存的字符变量,还有权值(在这里权值指的是字符出现的次数)。

classHfmNode

{

private:

char Date;    //树中的数据

int weight;   //结点的权值

HfmNode*lchild, *rchild;

public:

void setDate(char c) {           //设置  树中数据

    this->Date = c;M

}

void setWeight(int weight) {       //设置  权值

    this->weight = weight;

}

void setLchild(HfmNode* p) {       //设置 左孩子

    this->lchild = p;

}

void setRchild(HfmNode *p) {         //设置 右孩子

    this->rchild = p;

}

int getWeight() {            //获得权值的大小

    return weight;

}

char& getData() {             //获得结点的数据

    return this->Date;

}

HfmNode*& getLchild() {      //获得左孩子地址

    return this->lchild;

}

HfmNode*& getRchild() {       //获得右孩子地址

    return this->rchild;

}

//运算符重载来实现权值大小的比较

friend bool operator < (HfmNode &obj1,HfmNode obj2)

{

    return obj1.weight < obj2.weight;

}

//重载输入输出函数

friend ostream&operator<<(ostream& out, HfmNode& obj)

{

    out << obj.Date <<"\t";

    return out;

}

friend istream&operator>>(istream& in, HfmNode& obj)

{

    in >> obj.Date;

    return in;

}

}

树的类( class ht_Tree ),用于构造哈夫曼树,实现基本的树的功能

在构造哈夫曼树的函数里,传参数是一段任意的字符串,首先对字符串进行权值的计算,然后这些字符不停的入队,入队完成后就是核心部分——出队,构造父节点,再入队,一直循环, 哈夫曼树就构造完成。

class ht_Tree     //在该类可以存储树结点的数据类型

{

private:

   HfmNode* forest[MAXSIZE];

private:

   HfmNode* root;//树的根

   intlengh;//结点的数量

public:

   ht_Tree(){

      this->lengh= 0;//初始化树的结点的数量为NULL

      this->root= NULL;//初始化根节点为NULL

   }

   HfmNode*getRoot()      //根结点的获取

   {

      returnthis->root;

   }


    //创建树

   HfmNode*buildTree(const char* str) {


      intt = 0;

      inti, j, sign = 0, count = 0;

      intpriority[MAXSIZE] = { 0 };  //用于存放计算好的权值

      priorityQueue que;    //创建一个队列对象,用于将树结点入队

      HfmNode*temp = NULL, *ltemp = NULL, *rtemp = NULL;



      //统计权值的同时将结点入队

      for(i = 0; i < MAXSIZE; i++) {   //将所有的ASSIC值和字符串的每个字符进行比较


         for(j = 0; str[j] != '\0'; j++) {

            if(i == str[j]) {

               sign= 1;

               count++;//权值增加

            }

         }

         if(sign == 1) {

            temp= new HfmNode;        //申请一个树结点

            temp->setDate(i);          //设置数据

            temp->setWeight(count);    //设置权值

            priority[t++]= count;

            temp->setLchild(NULL);

            temp->setRchild(NULL);

            sign= 0;

            que.insertQueue(*temp);

            count= 0;

            j= 0;

         }

      }

      que.print();  //用于测试


      while(que.getNumOfQueue() > 1) {   //不停的出队形成父节点,然后父节点入队

         temp= new HfmNode;             //申请父节点

         ltemp= new HfmNode;            //申请左孩子

         rtemp= new HfmNode;            //申请右孩子

         //出队权值最小的两个结点给临时的左右孩子

         *ltemp= que.pop();

         *rtemp= que.pop();

         //设置父节点的权值 左孩子和右孩子

         temp->setWeight(ltemp->getWeight()+ rtemp->getWeight());

         temp->setLchild(ltemp);

         temp->setRchild(rtemp);

         //父节点入队

         que.insertQueue(*temp);

      }


      temp= new HfmNode;//申请根结点

      *temp= que.pop();//

      returntemp;//返回根节点

   }


};

下面就是编码的部分,因此设计了编码类。

编码类( class codeNode ),思路借用课本中的,即左孩子不为空 路径写1,右孩子为不为空路径写0 ,这一部分用递归 实现,存储后也利用队列模板进行入队 ,输出编码时只需要出队就行。

//字符编码类

class codeNode

{

private:

  char key;

  charcode[MAXSIZE];//保存字符编码

public:


  //实现原理递归的使用

  voidencode(HfmNode *tree, char *code, int i, priorityQueue &table)

  {  //如果递归到左孩子和右孩子都为NULL则将字符编码

     if(!tree->getLchild() && !tree->getRchild()) {

        code[i]= '\0';

        codeNode*temp = new codeNode;    //创建编码对象来保存每个叶子结点的编码

        temp->key= tree->getData();      //将该叶子结点保存的字符给编码类里的字符

        strcpy(temp->code,code);

        //为了能够实现一个字符对应一个编码,利用队列里存储将该结点逐个入队

        table.insertQueue(*temp);//将编码入队

     }

     else {

        //递归左孩子写编码

        if(tree->getLchild()) {

           code[i]= '0';  //如果左孩子不为NULL则路径设置为字符‘0’

           encode(tree->getLchild(),code, i + 1, table);

        }

        //递归右孩子写编码

        if(tree->getRchild()) {

           code[i]= '1';  //如果右孩子不为NULL则路径设置为字符‘1’

           encode(tree->getRchild(),code, i + 1, table);

        }

     }

  }

  //通过不停的出队进行打印字符以及对应的编码

  voidprintfTable(priorityQueue table)

  {

     while(table.getNumOfQueue() != 0) {   //如果队列里的数据不为0,则继续出队,进行循环

        codeNodetemp = table.pop();       //出队

        cout<< temp.key << ':' << temp.code << endl;

     }

  }

  //根据编码找出所代表的字符  实现原理(递归)

  voiddecode(HfmNode* tree, char* str, int i)

  {

     if ('0' ==str[i])     //对应的字符为0 则递归左孩子

        decode(tree->getLchild(),str, i + 1);

     if ('1' ==str[i])    //对应的字符为1,则递归右孩子

        decode(tree->getRchild(),str, i + 1);

     if(!str[i])          //最终无字符则输出最终的孩子所对应的数据

        cout<< tree->getData();

  }

  friend booloperator < (codeNode a, codeNode b)

  {

     returntrue;    

  }


};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值