哈夫曼编码问题

Description

给定一个数字N,代表有N种不同的字符,已知每种字符的出现次数,现在要求你设计一种编码,使得任意一个编码都不是另外一个编码的前缀,并且使得这些字符经过编码压缩之后的总长度最小; 

Input

一个数字N 代表有多少种不同的字符(0<=N<=1000)N个数字  每个数字代表一种字符的出现次数

Output

一个数字,代表总的编码长度

Sample Input

5
1
2
3
4
5

3
3
8
8

Sample Output

33
30

正常版

#include<bits/stdc++.h>
using namespace std;
bool bj[2001];
struct jg{
    int l,r,q;
};
struct nsj
{
    int n1,n2;
};
int data[2001],cd[2001];
jg shu[2001];
void dfs(int x,int y);
nsj zhapy(int x);
int main()
{
    int n,i,cnt;
    nsj n12;
    while(cin>>n)
    {
        memset(cd,0,sizeof(cd));
        memset(shu,0,sizeof(shu));
        for(i=1;i<=n;i++)
        {
            cin>>data[i];
        }
        for(i=1;i<=n;i++)
        {
            bj[i]=true;
            bj[i+n]=false;
        }
        cnt=n;
        while(cnt<2*n-1)
        {
            n12=zhapy(cnt);
            bj[n12.n1]=bj[n12.n2]=false;
            cnt++;
            bj[cnt]=true;
            shu[cnt].l=n12.n1;
            shu[cnt].r=n12.n2;
            shu[cnt].q=data[n12.n1]+data[n12.n2];
            data[cnt]=data[n12.n1]+data[n12.n2];
        }
        dfs(2*n-1,0);
        int ans=0;
        for(i=1;i<=n;i++)
        {
            ans+=cd[i]*data[i];
        }
        cout<<ans<<endl;
    }
    return 0;
}
nsj zhapy(int x)
{
    nsj cnt;
    int x1,x2,i;
    x1=999999999;
    x2=999999999;
    cnt.n1=1;
    cnt.n2=1;
    for(i=1;i<=x;i++)
    {
        if(bj[i])
        {
            if(data[i]<x1)
        {
            x2=x1;
            cnt.n2=cnt.n1;
            x1=data[i];
            cnt.n1=i;
        }
        else if(data[i]<x2)
        {
            cnt.n2=i;
            x2=data[i];
        }
        }

    }
    return cnt;
}
void dfs(int x,int y)
{
    int l,r;
    l=shu[x].l;
    if(l!=0)
    {
        dfs(l,y+1);
        r=shu[x].r;
        dfs(r,y+1);
    }
    else
    {
        cd[x]=y;
    }
}

变态版:

#include<bits/stdc++.h>
using namespace std;bool bj[2001];struct jg{int l,r,q;};struct nsj{int n1,n2;};int data[2001],cd[2001];jg shu[2001];void dfs(int x,int y);nsj zhapy(int x);int main(){int n,i,cnt;nsj n12;while(cin>>n){memset(cd,0,sizeof(cd));memset(shu,0,sizeof(shu));for(i=1;i<=n;i++){cin>>data[i];}for(i=1;i<=n;i++){bj[i]=true;bj[i+n]=false;}cnt=n;while(cnt<2*n-1){n12=zhapy(cnt);bj[n12.n1]=bj[n12.n2]=false;cnt++;bj[cnt]=true;shu[cnt].l=n12.n1;shu[cnt].r=n12.n2;shu[cnt].q=data[n12.n1]+data[n12.n2];data[cnt]=data[n12.n1]+data[n12.n2];}dfs(2*n-1,0);int ans=0;for(i=1;i<=n;i++){ans+=cd[i]*data[i];}cout<<ans<<endl;}return 0;}nsj zhapy(int x){nsj cnt;int x1,x2,i;x1=999999999;x2=999999999;cnt.n1=1;cnt.n2=1;for(i=1;i<=x;i++){if(bj[i]){if(data[i]<x1){x2=x1;cnt.n2=cnt.n1;x1=data[i];cnt.n1=i;}else if(data[i]<x2){cnt.n2=i;x2=data[i];}}}return cnt;}void dfs(int x,int y){int l,r;l=shu[x].l;if(l!=0){dfs(l,y+1);r=shu[x].r;dfs(r,y+1);}else{cd[x]=y;}}
贪心算法在哈夫曼编码问题中,核心思想是每次都选择权值最小的两个节点合并成一个新节点,直到所有节点合并成一棵树。以下是使用C语言实现哈夫曼编码问题的贪心算法的详细步骤和代码示例: ### 步骤 1. **定义哈夫曼树节点结构**:每个节点包含权值、左右子节点指针等信息。 2. **构建最小堆**:用于高效地选择两个权值最小的节点。 3. **构建哈夫曼树**:不断从最小堆中取出两个权值最小的节点,合并成一个新节点,再插入最小堆,直到堆中只剩一个节点。 4. **生成哈夫曼编码**:通过遍历哈夫曼树,为每个叶子节点生成对应的哈夫曼编码。 ### 代码示例 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> // 定义哈夫曼树节点结构 typedef struct HuffmanNode { char data; int freq; struct HuffmanNode *left, *right; } HuffmanNode; // 定义最小堆结构 typedef struct { int size; int capacity; HuffmanNode **array; } MinHeap; // 创建新的哈夫曼节点 HuffmanNode* newNode(char data, int freq) { HuffmanNode* node = (HuffmanNode*)malloc(sizeof(HuffmanNode)); node->data = data; node->freq = freq; node->left = node->right = NULL; return node; } // 创建最小堆 MinHeap* createMinHeap(int capacity) { MinHeap* minHeap = (MinHeap*)malloc(sizeof(MinHeap)); minHeap->size = 0; minHeap->capacity = capacity; minHeap->array = (HuffmanNode**)malloc(capacity * sizeof(HuffmanNode*)); return minHeap; } // 交换两个哈夫曼节点 void swapHuffmanNode(HuffmanNode** a, HuffmanNode** b) { HuffmanNode* t = *a; *a = *b; *b = t; } // 最小堆化 void minHeapify(MinHeap* minHeap, int idx) { int smallest = idx; int left = 2 * idx + 1; int right = 2 * idx + 2; if (left < minHeap->size && minHeap->array[left]->freq < minHeap->array[smallest]->freq) smallest = left; if (right < minHeap->size && minHeap->array[right]->freq < minHeap->array[smallest]->freq) smallest = right; if (smallest != idx) { swapHuffmanNode(&minHeap->array[smallest], &minHeap->array[idx]); minHeapify(minHeap, smallest); } } // 检查堆的大小是否为1 int isSizeOne(MinHeap* minHeap) { return minHeap->size == 1; } // 提取最小堆的根节点 HuffmanNode* extractMin(MinHeap* minHeap) { HuffmanNode* temp = minHeap->array[0]; minHeap->array[0] = minHeap->array[minHeap->size - 1]; --minHeap->size; minHeapify(minHeap, 0); return temp; } // 插入新节点到最小堆 void insertMinHeap(MinHeap* minHeap, HuffmanNode* node) { ++minHeap->size; int i = minHeap->size - 1; while (i && node->freq < minHeap->array[(i - 1) / 2]->freq) { minHeap->array[i] = minHeap->array[(i - 1) / 2]; i = (i - 1) / 2; } minHeap->array[i] = node; } // 构建最小堆 void buildMinHeap(MinHeap* minHeap) { int n = minHeap->size - 1; int i; for (i = (n - 1) / 2; i >= 0; --i) minHeapify(minHeap, i); } // 判断是否是叶子节点 int isLeaf(HuffmanNode* root) { return !(root->left) && !(root->right); } // 创建并构建最小堆 MinHeap* createAndBuildMinHeap(char data[], int freq[], int size) { MinHeap* minHeap = createMinHeap(size); for (int i = 0; i < size; ++i) minHeap->array[i] = newNode(data[i], freq[i]); minHeap->size = size; buildMinHeap(minHeap); return minHeap; } // 构建哈夫曼树 HuffmanNode* buildHuffmanTree(char data[], int freq[], int size) { HuffmanNode *left, *right, *top; MinHeap* minHeap = createAndBuildMinHeap(data, freq, size); while (!isSizeOne(minHeap)) { left = extractMin(minHeap); right = extractMin(minHeap); top = newNode('$', left->freq + right->freq); top->left = left; top->right = right; insertMinHeap(minHeap, top); } return extractMin(minHeap); } // 打印哈夫曼编码 void printCodes(HuffmanNode* root, int arr[], int top) { if (root->left) { arr[top] = 0; printCodes(root->left, arr, top + 1); } if (root->right) { arr[top] = 1; printCodes(root->right, arr, top + 1); } if (isLeaf(root)) { printf("%c: ", root->data); for (int i = 0; i < top; ++i) printf("%d", arr[i]); printf("\n"); } } // 哈夫曼编码主函数 void HuffmanCodes(char data[], int freq[], int size) { HuffmanNode* root = buildHuffmanTree(data, freq, size); int arr[100], top = 0; printCodes(root, arr, top); } // 主函数 int main() { char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'}; int freq[] = {5, 9, 12, 13, 16, 45}; int size = sizeof(arr) / sizeof(arr[0]); HuffmanCodes(arr, freq, size); return 0; } ``` ### 代码解释 1. **`newNode` 函数**:创建一个新的哈夫曼节点。 2. **`createMinHeap` 函数**:创建一个最小堆。 3. **`minHeapify` 函数**:对最小堆进行堆化操作。 4. **`extractMin` 函数**:从最小堆中提取最小元素。 5. **`insertMinHeap` 函数**:向最小堆中插入元素。 6. **`buildHuffmanTree` 函数**:构建哈夫曼树。 7. **`printCodes` 函数**:打印每个字符的哈夫曼编码。 8. **`HuffmanCodes` 函数**:调用 `buildHuffmanTree` 和 `printCodes` 完成哈夫曼编码的生成和打印。 ### 复杂度分析 - **时间复杂度**:$O(nlogn)$,其中 $n$ 是字符的数量。主要是因为每次从最小堆中提取最小元素和插入新元素的操作时间复杂度为 $O(logn)$,需要进行 $n - 1$ 次合并操作。 - **空间复杂度**:$O(n)$,主要用于存储哈夫曼树的节点。 通过上述代码和步骤,就可以使用C语言实现哈夫曼编码问题的贪心算法。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值