Introduce to algorithm--------pseudo code to C/C++ code (chapter 16)

贪心算法

贪心算法(greedy algorithm)就是这样的算法,它在每一步都做出在当时看起来最佳的选择。也就是说,它总是做出局部最优的选择,寄希望这样的选择能导致全局最优解。

但需要注意的是:

贪心算法并不保证得到最优解。

当将贪心算法应用于特殊的一个问题时,需要特别注意贪心性质的正确性:它是否能保证得到最优解。
所有的算法其实并不是书上描述的那样简单,需要对问题的理解、算法的分析、算法的证明。The idea inside algorithm is more important。

活动选择问题

问题描述见算法导论,这里直接给出代码:

/*!
 * @param   s   a pointer point to an array containing task begin times
 * @param   f   a pointer point to an array containing task end times
 * @param   k   an array index indicates the beginning of sub-question
 * @param   n   an number indicates the scale of question(namely how much elements inside former array)
 * @return  vector<int>     a vector containing task index of answers
 */
vector<int> RECURSIVE_ACTIVITY_SELECTOR (int *s, int*f, int k, int n)
{
    int m = k + 1;
    //find the first activity in Sk to finish
    while (m <= n && s[m] < f[k])
        ++m;

    auto result = vector<int> ();
    if (m <= n)
    {
        result.push_back (m);
        for (auto const& it: RECURSIVE_ACTIVITY_SELECTOR (s, f, m, n))
            result.push_back (it);
    }
    //return null if none
    return result;
}

书中已证明贪心算法的可行性,这里不多做介绍。该版本的迭代版本也不在这里给出,思路是一样的,只是将尾递归化为迭代。

Huffman code

以下是简介:

赫夫曼编码可以很有效的压缩数据:通常可以节省20% ~ 90%的空间,具体压缩率依赖于数据的特性。我们将待压缩数据看作字节序列。根据每个字符的出现频率,赫夫曼贪心算法构造出字符的最优二进制表示。

我觉得下面一张图很好的表达了赫夫曼编码的精髓:
pircure

表达信息的方式有很多种,这里涉及到二进制字符编码, 即将字符用二进制码来表示,产生一个从字符到二进制的映射。如图所述,采用变长编码 可能定长编码会有更好的压缩率。实际上,该种编码方式依赖于字符的已知信息—-出现频率。在频率已知的情况下有较好的压缩率。

我们这里只考虑所谓的前缀码,即没有任何码字是其它码字的前缀。

前缀码确实可以保证达到最优数据压缩率.

前缀码的作用是简化解码过程由于没有码字是其它码字的前缀,编码文件的开始字码是无歧义的。我们可以简单的识别出开始字码,将其转换回原字符,然后对编码文件剩余部分重复这种解码过程。

这里简单解释,若不是前缀码,在编码时不会有多余的工作,但在解码时必须处理前缀问题。即一个码字可能是一个字符的码字,也可能是另一个字符的前缀,因而在解码时必须处理这两种情况。

解码过程需要前缀码的一种方便的表示形式,以便我们可以容易的截取开始码字。一种二叉树表示可以满足这种需求,其中叶节点为给定的字符。字符的二进制码字用从根结点到该字符叶节点的简单路径表示,其中0意味着转向“左孩子”,1意味着转向“右孩子”.

如图分别为定长编码和变长编码的二叉树表示:
picture

可以看到,变长编码主要是利用字符出现频率已知的信息,将频率高的字符放在二叉树高处,减少二进制码字的长度,相对的,频率低的字符的二进制码字长度变大 。所以说,采用变长编码只是可能比定长编码有较好的压缩率。
以下为代码:

/*node containing frequent number, there omit character,
 please compare the freq num to recognize*/
typedef struct node_tag
{
    node_tag        *left,
                    *right;
    int             freq;
}Node, *PNode;


//extract node pointer containing minimun frequent number
PNode EXTRACT_MIN (vector<PNode>& Q)
{
    //assume not empty
    PNode   ptr = Q.front ();
    auto    iterator = Q.begin ();

    //pick minimun
    for (auto it = Q.begin (); it != Q.end (); ++it)
        if ((*it)->freq < ptr->freq)
            ptr = *it,
            iterator = it;

    //erase the one picked
    Q.erase (iterator);
    return ptr;
}

VOID INSERT (vector<PNode>& Q, PNode z)
{
    Q.push_back (z);
}

PNode Huffman (int *array, int num)
{
    //initial
    vector<PNode>           Q;
    for (int i = 0; i < num; ++i)
    {
        PNode   z = new Node ();
        z->left = nullptr;
        z->right = nullptr;
        z->freq = array[i];
        Q.push_back (z);
    }

    //core body
    for (int i = 1; i < num; ++i)
    {
        PNode   z = new Node ();
        z->left = EXTRACT_MIN (Q);
        z->right = EXTRACT_MIN (Q);
        z->freq = z->left->freq + z->right->freq;
        INSERT (Q, z);
    }
    return EXTRACT_MIN (Q);
}

//!!!!!!!! 代码仅作示范,未释放堆内存 !!!!!!!!!//

因为频率低的字符在二叉树地处,所以Huffman(...)函数自底向上构建二叉树.代码不再赘述。
Fuffman(...)采用贪心算法,其正确性在算法导论中给出了证明。

详见算法导论

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值