DS二叉树--赫夫曼树的构建与编码 (二维数组理解)

题目描述

给定n个权值,根据这些权值构造huffman树,并进行huffman编码

参考课本算法,注意数组访问是从位置1开始

要求:赫夫曼的构建中,默认左孩子权值不大于右孩子权值

输入

第一行输入t,表示有t个测试实例
第二行先输入n,表示第1个实例有n个权值,接着输入n个权值,权值全是小于1万的正整数
依此类推

输出

逐行输出每个权值对应的编码,格式如下:权值-编码
即每行先输出1个权值,再输出一个短划线,再输出对应编码,接着下一行输入下一个权值和编码。
以此类推

输入样例1
1
5 15 4 4 3 2

输出样例1
15-1
4-010
4-011
3-001
2-000

赫夫曼编码

构建赫夫曼树

构建一个子树的步骤,以下循环 n - 1 次,就可以把一片森林合并成一颗树。

初始化最小和次小值

  • 内部循环遍历森林的节点 (forest[j]),找到第一个非空节点作为 minn,接着找到下一个非空节点作为 minnSub,这两个节点分别表示初始的最小和次小权重节点。

更新最小和次小值

  • minnSub 位置开始遍历森林,更新 minnminnSub 的值。
  • 如果当前节点权重小于 minn 的权重,则将 minnminnSub 交换,并更新最小值为当前节点。
  • 如果当前节点权重介于 minnminnSub 之间,则将其赋值给 minnSub

建立赫夫曼树:

  • 创建一个rroot 节点,并将权重赋值为 minn + minnSub
  • minnSub 赋为 NULL , 将 minn 赋值为 temp

在这里插入图片描述

HNode* createHuffmanTree(int arr[], int n) {
	HNode* rroot = NULL;
    
   // 初始化森林
  HNode** forest = new HNode*[n];  
  for (int i = 0; i < n; i++) {  
    HNode* temp;
    temp = new HNode;
    temp->weight = arr[i];
    // 将一个地址赋给了 forest[i]:
    forest[i] = temp;`在这里插入代码片`
  }
    
  for(int i=0;i<n-1;i++){
    // 选择两个结点:
    int min = -1,subMin = -1;
    choose(forest,min,subMin,n);
      
    // 创建新的树:
    rroot = new HNode;  // 一定要在这里开空间。
    rroot->weight = forest[min]->weight + forest[subMin]->weight;
    rroot->lchild = forest[min];
    rroot->rchild = forest[subMin];
    forest[min] = rroot;
    forest[subMin] = NULL;
  }
  
  return rroot;
}

选择结点的函数

逻辑并不复杂,但是写起来很容易错

void choose(HNode** forest,int& min,int& subMin,const int n)
{
  // 找到一个非空结点赋给 min,初始化
  for(int i=0;i<n;i++){
    if(forest[i]){
      min = i;
      for(int j=i+1;j<n;j++){
        if(forest[j]&&forest[j]->weight>=forest[i]->weight){
          subMin = j;
          break;
        }else if(forest[j]&&forest[j]->weight<forest[i]->weight){
          subMin = i;
          min = j;
          break;
        }
      }
      break;
    }
  }
  // cout<<"min = "<<min<<" subMin = "<<subMin<<endl;
  for(int i=subMin+1;i<n;i++){
    if(!forest[i]) continue;
    if(forest[i]->weight<forest[min]->weight){
      subMin = min;
      min = i;
    }else if(min!=i&&forest[i]->weight<forest[subMin]->weight&&forest[i]->weight>=forest[min]->weight){
      subMin = i;
    }
  }
  // cout<<"After Update: "<<"min = "<<min<<" subMin = "<<subMin<<endl;

}

哈夫曼编码

我是利用前序遍历编码的。。

void huffmanCoding( HNode*& node,string code ="") 
{
    if(node == NULL) return;
    node->Hconding = code;
    huffmanCoding(node->lchild,code + "0");
    huffmanCoding(node->rchild,code + "1");
}

输出

  • 根据数组的顺序在赫夫曼树中寻找它的位置
  • 为了避免重复输出,找到之后将权重赋值为-1
  • 关键就在于如何在赫夫曼树中找到这个位置,我用的是前序遍历搜索
HNode* Find(HNode*& node, int target)
{
  HNode* root = NULL;  // 初始化返回值为空指针
  if (!node) return root;  // 如果当前节点为空,返回空指针(递归终止条件)

  if (node->weight == target) {  // 如果当前节点的权重等于目标值,返回当前节点
    // node = NULL;
    node->weight = -1;
    return node;
  } 

  // 递归查找左子树
  root = Find(node->lchild, target);
  
  // 如果在左子树中找到了目标节点,直接返回
  if (root != NULL) {
    return root;
  }
  
  // 递归查找右子树
  root = Find(node->rchild, target);

  return root;  // 返回找到的节点(如果有的话)
}

代码

# include <iostream>
# include <string>

using namespace std;

struct HNode
{
    int weight;
    HNode* lchild, * rchild;
    string Hconding; // 存储编码
    HNode(int w = 0, HNode* l = NULL, HNode* r = NULL) : weight(w), lchild(l), rchild(r), Hconding("") {}
};

void choose(HNode** forest,int& min,int& subMin,const int n)
{
  // 找到一个非空结点赋给 min,初始化
  for(int i=0;i<n;i++){
    if(forest[i]){
      min = i;
      for(int j=i+1;j<n;j++){
        if(forest[j]&&forest[j]->weight>=forest[i]->weight){
          subMin = j;
          break;
        }else if(forest[j]&&forest[j]->weight<forest[i]->weight){
          subMin = i;
          min = j;
          break;
        }
      }
      break;
    }
  }
  // cout<<"min = "<<min<<" subMin = "<<subMin<<endl;
  for(int i=subMin+1;i<n;i++){
    if(!forest[i]) continue;
    if(forest[i]->weight<forest[min]->weight){
      subMin = min;
      min = i;
    }else if(min!=i&&forest[i]->weight<forest[subMin]->weight&&forest[i]->weight>=forest[min]->weight){
      subMin = i;
    }
  }
  // cout<<"After Update: "<<"min = "<<min<<" subMin = "<<subMin<<endl;

}

HNode* createHuffmanTree(int arr[], int n) {
  HNode** forest = new HNode*[n];  // 初始化森林
  HNode* rroot = NULL;
  for (int i = 0; i < n; i++) {  
    HNode* temp;
    temp = new HNode;
    temp->weight = arr[i];
    forest[i] = temp;
  }

  for(int i=0;i<n-1;i++){
    // 选择两个结点:
    int min = -1,subMin = -1;
    choose(forest,min,subMin,n);
    // 创建新的树:
    rroot = new HNode;  // 一定要在这里开空间。
    rroot->weight = forest[min]->weight + forest[subMin]->weight;
    rroot->lchild = forest[min];
    rroot->rchild = forest[subMin];
    forest[min] = rroot;
    forest[subMin] = NULL;
  }
  
  return rroot;
}

void huffmanCoding( HNode*& node,string code ="") 
{
    if(node == NULL) return;
    node->Hconding = code;
    huffmanCoding(node->lchild,code + "0");
    huffmanCoding(node->rchild,code + "1");
}

// 利用前序遍历搜索:
HNode* Find(HNode*& node, int target)
{
  HNode* root = NULL;  // 初始化返回值为空指针
  if (!node) return root;  // 如果当前节点为空,返回空指针(递归终止条件)

  if (node->weight == target) {  // 如果当前节点的权重等于目标值,返回当前节点
    // node = NULL;
    node->weight = -1;
    return node;
  } 

  // 递归查找左子树
  root = Find(node->lchild, target);
  
  // 如果在左子树中找到了目标节点,直接返回
  if (root != NULL) {
    return root;
  }
  
  // 递归查找右子树
  root = Find(node->rchild, target);

  return root;  // 返回找到的节点(如果有的话)
}



int main()
{
    int t;
    cin >> t;
    while(t--){
        int n;
        cin>>n;
        int* arr = new int [n];
        for(int i = 0; i < n; i++){
            cin >> arr[i];
        }
        HNode* root = createHuffmanTree(arr, n);
        huffmanCoding(root);
        // 输出,我实在是不知道这个输出的顺序是怎么样的::
        for(int i=0;i<n;i++){
          cout<<arr[i]<<"-"<<Find(root,arr[i])->Hconding<<endl;
        }
    }
}

关于内存分配一点小问题(初始化森林)

  • forset 数组里面存放的是地址,因而是一个二维数组。
  • 下面temp循环开空间,并把地址赋给forest[i]就是给二维数组开第二个空间。
  HNode** forest = new HNode*[n];  // 初始化森林

  for (int i = 0; i < n; i++) {  
    HNode* temp;
    temp = new HNode;
    temp->weight = arr[i];
    forest[i] = temp;

  }
  • 如果不使用temp
  HNode** forest = new HNode*[n];  // 初始化森林

  for(int i=0;i<n;i++)
    forest[i] = new HNode;

  for (int i = 0; i < n; i++) {  
    forest[i]->weight = arr[i];
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值