贪心算法-哈夫曼编码

《算法导论》引理16.2:令C为一个字母表,其中每个字符c属于C都有一个频率c.freq。令x和y是C中频率最低的两个字符,那么存在C的一个最优前缀码,x和y的码字长度相同,且只有最后一个二进制位不同。(证明问题具有贪心选择性质

引理16.3 令C为一个给定的字符表,其中每个字符c属于C都定义了一个频率c.freq。令x和y是C中频率最低的两个字符。令C1为C去掉字符x和y,加入一个新字符z后得到的字母表。这里z.freq=x.freq+y.freq。令T1为C1的任意一个最优前缀对应的编码树。于是我们将T1中叶结点z替换为一个以x和y为孩子的内部结点,得到树T,T表示字母表C的一个最优前缀码。(证明了构造最优前缀码的问题具有最优子结构性质

字母表每个元素的结构

struct word {
    char c;
    int freq;
    string b;
    string code;
    int parents;
    int left;
    int right;
    word() {
        c = ' ';
        parents = -1;
        left = -1;
        right = -1;
    }
    word(char ch, int f) :c(ch), freq(f) {
        parents=-1;
        left=-1;
        right=-1;
    }
};
//构造哈夫曼树
word huffman(word *w,int n) {
    int s = n;
    for (int i = 1; i <= n-1; i++) {
        int minL = 0, minR = 0;
        for (int j = 1; j <= s; j++) {
            //数组中的最小值
            if (w[j].freq < w[minL].freq&&w[j].parents == -1)
                minL = j;
        }
        for (int j = 1; j <= s; j++) {
            //数组中的次小值(可以与最小值相等)
            if (w[j].freq >= w[minL].freq&&w[j].freq<w[minR].freq&&j!=minL&&w[j].parents == -1)
                minR = j;
        }
        s++;
        w[s].freq = w[minL].freq + w[minR].freq;
        w[s].left = minL;
        w[s].right = minR;
        w[minL].parents = s;
        //左孩子与父结点连接的边对应编码0
        w[minL].b = "0";
        w[minR].parents = s;
        //右孩子与父结点连接的边对应编码1
        w[minR].b = "1";
    }
    //if (s == 2 * n - 1) cout << "-----------right";
    cout << w[s].freq;
    return w[s - 1];
}

void huffmanCode(word *w,int n) {
    for (int i = 1; i <= n; i++) {
        int j = i;
        while (w[j].parents != -1) {
            w[i].code += w[j].b;
            j = w[j].parents;
        }
        //因为上面的循环过程是从叶子结点向根节点遍历,因此最后得到的编码要将顺序调换
        reverse(w[i].code.begin(), w[i].code.end());
    }
}

int main() {
    //这个元素便于后面比较
    word w0(' ', 0x7fffffff);
    word w1('a', 45);
    word w2('b', 13);
    word w3('c', 12);
    word w4('d', 16);
    word w5('e', 9);
    word w6('f', 5);
    int n = 6;
    //哈夫曼树一定有2*n-1个结点,将树的结点都存到数组w中
    word w[12] = { w0,w1,w2,w3,w4,w5,w6 };
    huffman(w, 6);
    huffmanCode(w, 6);
    return 0;

}
点赞