堆与优先队列、哈夫曼树(贪心算法)

Posted on 2019-08-18 15:16  金色的省略号  阅读(519)  评论(0编辑  收藏  举报

  一、哈夫曼树最优二叉树,WPL最小的二叉树 

  带权路径长度(WPL):

  设二叉树有n个叶子结点,每个叶子结点带 有权值 wk,从根结点到每个叶子结点的长度为 lk,则每个叶子结点的带权路径长度之和WPL

  哈夫曼树的特点: 

  1、没有度为1的结点

  2、哈夫曼树的任意非叶节点的左右子树交换后仍是哈夫曼树

  3、n个叶子结点的哈夫曼树共有2n-1个结点(由没有度为1的结点推出, n0+n1+n2 = 2*n0-1)

  4、对同一组权值{w1 ,w2 , …… , wn},存在不同构的两棵哈夫曼树;

  5、外部结点的个数比内部结点的个数多一;

  二、堆的两个特性

结构性:用数组表示的完全二叉树; 

有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)

(堆的结点的子结点之间没有关系)

  1、堆的操作

  1 typedef struct HNode *Heap; /* 堆的类型定义 */
  2 struct HNode {
  3     ElementType *Data; /* 存储元素的数组 */
  4     int Size;          /* 堆中当前元素个数 */
  5     int Capacity;      /* 堆的最大容量 */
  6 };
  7 typedef Heap MaxHeap; /* 最大堆 */
  8 typedef Heap MinHeap; /* 最小堆 */
  9  
 10 #define MAXDATA 1000  /* 该值应根据具体情况定义为大于堆中所有可能元素的值 */
 11  
 12 MaxHeap CreateHeap( int MaxSize )
 13 { /* 创建容量为MaxSize的空的最大堆 */
 14  
 15     MaxHeap H = (MaxHeap)malloc(sizeof(struct HNode));
 16     H->Data = (ElementType *)malloc((MaxSize+1)*sizeof(ElementType));
 17     H->Size = 0;
 18     H->Capacity = MaxSize;
 19     H->Data[0] = MAXDATA; /* 定义"哨兵"为大于堆中所有可能元素的值*/
 20  
 21     return H;
 22 }
 23  
 24 bool IsFull( MaxHeap H )
 25 {
 26     return (H->Size == H->Capacity);
 27 }
 28  
 29 bool Insert( MaxHeap H, ElementType X )
 30 { /* 将元素X插入最大堆H,其中H->Data[0]已经定义为哨兵 */
 31     int i;
 32   
 33     if ( IsFull(H) ) { 
 34         printf("最大堆已满");
 35         return false;
 36     }
 37     i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */
 38     for ( ; H->Data[i/2] < X; i/=2 )
 39         H->Data[i] = H->Data[i/2]; /* 上滤X */
 40     H->Data[i] = X; /* 将X插入 */
 41     return true;
 42 }
 43  
 44 #define ERROR -1 /* 错误标识应根据具体情况定义为堆中不可能出现的元素值 */
 45  
 46 bool IsEmpty( MaxHeap H )
 47 {
 48     return (H->Size == 0);
 49 }
 50  
 51 ElementType DeleteMax( MaxHeap H )
 52 { /* 从最大堆H中取出键值为最大的元素,并删除一个结点 */
 53     int Parent, Child;
 54     ElementType MaxItem, X;
 55  
 56     if ( IsEmpty(H) ) {
 57         printf("最大堆已为空");
 58         return ERROR;
 59     }
 60  
 61     MaxItem = H->Data[1]; /* 取出根结点存放的最大值 */
 62     /* 用最大堆中最后一个元素从根结点开始向上过滤下层结点 */
 63     X = H->Data[H->Size--]; /* 注意当前堆的规模要减小 */
 64     for( Parent=1; Parent*2<=H->Size; Parent=Child ) {
 65         Child = Parent * 2;
 66         if( (Child!=H->Size) && (H->Data[Child]<H->Data[Child+1]) )
 67             Child++;  /* Child指向左右子结点的较大者 */
 68         if( X >= H->Data[Child] ) break; /* 找到了合适位置 */
 69         else  /* 下滤X */
 70             H->Data[Parent] = H->Data[Child];
 71     }
 72     H->Data[Parent] = X;
 73  
 74     return MaxItem;
 75 } 
 76  
 77 /*----------- 建造最大堆 -----------*/
 78 void PercDown( MaxHeap H, int p )
 79 { /* 下滤:将H中以H->Data[p]为根的子堆调整为最大堆 */
 80     int Parent, Child;
 81     ElementType X;
 82  
 83     X = H->Data[p]; /* 取出根结点存放的值 */
 84     for( Parent=p; Parent*2<=H->Size; Parent=Child ) {
 85         Child = Parent * 2;
 86         if( (Child!=H->Size) && (H->Data[Child]<H->Data[Child+1]) )
 87             Child++;  /* Child指向左右子结点的较大者 */
 88         if( X >= H->Data[Child] ) break; /* 找到了合适位置 */
 89         else  /* 下滤X */
 90             H->Data[Parent] = H->Data[Child];
 91     }
 92     H->Data[Parent] = X;
 93 }
 94  
 95 void BuildHeap( MaxHeap H )
 96 { /* 调整H->Data[]中的元素,使满足最大堆的有序性  */
 97   /* 这里假设所有H->Size个元素已经存在H->Data[]中 */
 98  
 99     int i;
100  
101     /* 从最后一个结点的父节点开始,到根结点1 */
102     for( i = H->Size/2; i>0; i-- )
103         PercDown( H, i );
104 }
堆的操作,c代码

  2、堆的操作实例

堆的路径,将一系列给定数字插入一个初始为空的小顶堆H[]。

随后对任意给 定的下标`i`,打印从H[i]到根结点的路径。

  

 1 #include <stdio.h>
 2 /* 1.定义堆H */
 3 #define MAXN 1001
 4 #define MINH -10001
 5 int H[MAXN], size;//堆,堆的大小
 6 /* 2.堆的初始化 */
 7 void Create ()
 8 {
 9     size = 0;
10     H[0] = MINH;/*设置“岗哨”*/    
11 }
12 /* 3.堆插入 */
13 void Insert ( int X )
14 {
15     /* 省略检查堆是否已满 */
16     int i; //46 23 26 24 10
17     /* 
18         下标从1开始,逐个插入,
19         插入过程,从后向前,逐个与其父结点比较
20         父结点大, 上滤X
21         1  2  3  4  5 
22         46               <-46  1/2==0  0
23         23 46            <-23  2/2==1  1 0
24         23 46 26         <-26  3/2==1  1 0
25         23 24 26 46      <-24  4/2==2  2 1 0
26         10 23 26 46 24   <-10  5/2==2  2 1 0
27     */
28     for (i=++size; H[i/2] > X; i/=2)
29         H[i] = H[i/2];
30     H[i] = X;//将X插入H
31 }
32 
33 int main()
34 {    
35 /* 
36     读入n和m
37     根据输入序列建堆
38     对m个要求:打印到根的路径
39     5 3
40     46 23 26 24 10
41     5 4 3
42  */    
43     int n, m, x, i, j;
44     scanf("%d%d", &n, &m);
45     Create(); /* 堆初始化 */
46     for (i=0; i<n; i++) { 
47         scanf("%d", &x);
48         Insert(x); /*以逐个插入方式建堆 */
49     }
50     
51     /* for(int i=1; i<=n; ++i)
52         printf("%3d",H[i]); */
53     
54     /* m个结点 */
55     for (i=0; i<m; i++) 
56     {
57         scanf("%d", &j);//具体结点
58         printf("%d", H[j]);
59          /*向根方向输出各结点*/
60         while (j>1) {
61             j /= 2;
62             printf(" %d", H[j]);
63         }
64         printf("\n");
65     }
66     return 0;
67 }
最小堆,插入建堆

  三、C++  根据给定的权值创建,哈夫曼树  (用到优先队列,贪心算法,青岛大学mooc)

  学友代码

#include <iostream>
#include <map>
#include<algorithm>
#include <queue>
#define MAX 500
using namespace std;

int n;
//哈夫曼树中结点的类型
struct HuffmanTree
{
    char data;
    int weight;
    int parent, lchild, rchild;
};
HuffmanTree HT[MAX];


//y优先队列结点类型
struct NodeType
{
    int now;//对应哈夫曼树中的位置(数组下标)
    char data;
    int weight;
    bool operator<(const NodeType& s) const//(小顶堆)
    {
        return s.weight < weight;
    }
};

priority_queue<NodeType > qu;

void CreatHuffmanTree()
{
    NodeType e, e1, e2;
    cout << "请输入构建的哈夫曼树的元素以及对应权值:" << endl;
    for (int i = 0; i < n; i++)
    {
        cin >> HT[i].data >> HT[i].weight;
    }
    //将n个结点进队
    for(int i=0;i<n;i++)
    {
        e.now = i;
        e.data = HT[i].data;
        e.weight = HT[i].weight;
        qu.push(e);
    }
    //初始化结点的指针域
    for (int i = 0; i < 2*n-1; i++)
    {
        HT[i].parent = HT[i].lchild = HT[i].rchild = -1;
    }
    //构造huffman树剩余结点(求和得到的结点)
    for(int j=n;j<2*n-1;j++)
    {
        e1 = qu.top();//访问最小
        qu.pop();
        e2 = qu.top();//访问次小
        qu.pop();
        HT[j].weight = e1.weight + e2.weight;
        HT[j].lchild = e1.now;
        HT[j].rchild = e2.now;
        HT[e1.now].parent = j;//最小的结点的parent为n号位置上的结点
        HT[e2.now].parent = j;//次小也是
        e.now = j;//构造新结点
        e.weight = e1.weight + e2.weight;
        qu.push(e);
    }
}

void HuffmmanCode()
{
    string code;
    for(int i=0;i<n;i++)
    {
        code = "";
        int curnow = i;
        int f = HT[curnow].parent;//当前结点的parent不是根结点就往上找(直到到达根结点,就可以得到哈夫曼编码的倒序)
        while(f!=-1)//构造好的huffman树只有根节点没有parent,因此根节点的parent为-1       
        {
            if (HT[f].lchild == curnow)
                code += '0';
            else
                code += '1';
            curnow = f;
            
            f = HT[curnow].parent;
            
        }
        cout << HT[i].data << " ";
        reverse(code.begin(), code.end() );
        cout << code << endl;
    }
}

int main()
{
    
    cout << "请输入要构建哈夫曼树的元素个数:";
    freopen("d:\\1.txt","r",stdin );
    cin >> n;
    CreatHuffmanTree();
    cout << "哈夫曼编码为:" << endl;
    HuffmmanCode();
    return 0;
}
View Code

  修改青岛大学代码

#include <iostream>
 #include <vector>
 #include <queue>
 #include <map>
 using namespace std;
 struct Node{
     char ch;
     int data;
     Node* left;
     Node* right;
     Node(){ left = right= NULL;}
     Node(char ch, int data):ch(ch),data(data){
         left = right = NULL;
     }
 };
 struct HuffmanTree{
     Node* root;    
     HuffmanTree(){root = NULL;}
     HuffmanTree(char ch, int weight){
         root = new Node(ch,weight);        
     }
     HuffmanTree(vector<HuffmanTree> &nodes){
         priority_queue<HuffmanTree, vector<HuffmanTree>, 
             greater<HuffmanTree>> q(nodes.begin(),nodes.end());
         HuffmanTree tmp;
         for(int i=1; i<nodes.size();++i){
             tmp.root =  new Node();
             tmp.root->left = q.top().root; q.pop();
             tmp.root->right = q.top().root; q.pop();
             tmp.root->data = tmp.root->left->data + tmp.root->right->data;
             q.push(tmp);
         }
         root = q.top().root;
     }
     bool operator > (const HuffmanTree &t) const{
         return this->root->data > t.root->data;
     }
     void print(){
         rprint(root,0);
     }
     void rprint(Node* r, int depth){
         for(int i=0; i<depth; ++i) cout<<"  ";
         if(!r) cout<<"[/]"<<endl;
         else{
             cout<<r->data<<endl;
             rprint(r->left, depth+1);
             rprint(r->right,depth+1);
         }
     }
     //huffman编码
     map<string, char > HuffmanCode;
     void createHuffmanCode(Node* root, string tmp ){
         if(root->left==NULL && root->right==NULL ){
             HuffmanCode[tmp] = root->ch;
             return;
         }
         createHuffmanCode(root->left, tmp+="0");
         tmp.pop_back();
         createHuffmanCode(root->right, tmp+="1");
         tmp.pop_back();         
     }
     //打印编码      
     void printHuffmanCode(){
         string tmp;
         createHuffmanCode(root,tmp );
         map<char, string > t;
         for(map<string, char >::iterator it = HuffmanCode.begin();
                    it != HuffmanCode.end(); ++it )
            t[it->second] = it->first;
         for(map<char, string >::iterator it = t.begin();
                    it != t.end(); ++it )
            cout << it->first << " " << it->second << endl;
     }
 };
 
 int main()
 {
     freopen("d:\\1.txt","r",stdin );
     int nv,weight;
     char ch;
     cin>>nv;
     vector<HuffmanTree> nodes;
     for(int i=0; i<nv; ++i){
         cin>> ch >> weight;
         nodes.emplace_back(ch,weight);
     }
     HuffmanTree ht(nodes);
     //ht.print();
     ht.printHuffmanCode();
     return 0;
 }
View Code

  青岛大学代码

#include <iostream>
 #include <vector>
 #include <queue>
 using namespace std;
 struct Node{
     int data;
     Node* left;
     Node* right;
     Node(){ left = right= NULL;}
     Node(int data){
         this->data = data;
         left = right = NULL;
     }
 };
 struct HuffmanTree{
     Node* root;
     HuffmanTree(){root = NULL;}
     HuffmanTree(int weight){
         root = new Node(weight);        
     }
     HuffmanTree(vector<HuffmanTree> &nodes){
         priority_queue<HuffmanTree, vector<HuffmanTree>, 
             greater<HuffmanTree>> q(nodes.begin(),nodes.end());
         HuffmanTree tmp;
         for(int i=1; i<nodes.size();++i){
             tmp.root =  new Node();
             tmp.root->left = q.top().root; q.pop();
             tmp.root->right = q.top().root; q.pop();
             tmp.root->data = tmp.root->left->data + tmp.root->right->data;
             q.push(tmp);
         }
         root = q.top().root;
     }
     bool operator > (const HuffmanTree &t) const{
         return this->root->data > t.root->data;
     }
     void print(){
         rprint(root,0);
     }
     void rprint(Node* r, int depth){
         for(int i=0; i<depth; ++i) cout<<"  ";
         if(!r) cout<<"[/]"<<endl;
         else{
             cout<<r->data<<endl;
             rprint(r->left, depth+1);
             rprint(r->right,depth+1);
         }
     }
 };
 
 int main()
 {
     int nv,weight;
     cin>>nv;
     vector<HuffmanTree> nodes;
     for(int i=0; i<nv; ++i){
         cin>>weight;
         nodes.emplace_back(weight);
     }
     HuffmanTree ht(nodes);
     ht.print();
     return 0;
 }
 /*
7
10 15 12 3 4 13 1 
 */
View Code