日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 人文社科 > 生活经验 >内容正文

生活经验

贪心算法简单实践 -- 分糖果、钱币找零、最多区间覆盖、哈夫曼编解码

發(fā)布時(shí)間:2023/11/27 生活经验 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 贪心算法简单实践 -- 分糖果、钱币找零、最多区间覆盖、哈夫曼编解码 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1. 貪心算法概覽

貪心算法是一種算法思想。希望能夠滿足限制的情況下將期望值最大化。比如:Huffman編碼,Dijkstra單源最短路徑問(wèn)題,Kruskal最小生成樹(shù) 等問(wèn)題都希望滿足限制的情況下用最少的代價(jià)找到解決問(wèn)題的辦法。

這個(gè)辦法就是貪心算法的思想。
實(shí)際用貪心算法解決問(wèn)題可以有如下幾步:

  1. 當(dāng)看到這類(lèi)問(wèn)題時(shí) 我們能夠聯(lián)想到貪心算法:針對(duì)一組數(shù)據(jù),我們定義了期望值和限制值,希望在滿足限制值的情況下期望值最大。
    比如最短路徑中,限制值是每一次移動(dòng)只能向下或向右,期望值是走到終點(diǎn)(某個(gè)頂點(diǎn)),期望用最少的步數(shù)走到目標(biāo)頂點(diǎn)。
  2. 嘗試使用貪心算法來(lái)解決問(wèn)題:每一次做出選擇時(shí)在對(duì)限制值同等貢獻(xiàn)量的情況下,選擇對(duì)期望值貢獻(xiàn)最大的數(shù)據(jù)。
  3. 嘗試在案例數(shù)據(jù)中舉例,查看貪心方式的實(shí)驗(yàn)結(jié)果是否最優(yōu)。

貪心算法適用的案例 是 每一次的選擇都是獨(dú)立事件,不會(huì)受到之前選擇的影響。比如有權(quán)圖中的最短路徑的查找,當(dāng)前的選擇會(huì)對(duì)后續(xù)的選擇造成影響,第一次選擇最優(yōu)的,后續(xù)可選的權(quán)值都是特別大的,則期望值并不是最優(yōu)的。

可以看看如下幾個(gè)簡(jiǎn)單實(shí)踐。

2. 貪心算法實(shí)踐

2.1 分糖果

我們有m個(gè)糖果和n個(gè)孩子。我們現(xiàn)在要把糖果分給這些孩子吃,但是糖果少,孩子多(m<n),所以糖果只能分配給一部分孩子。

每個(gè)糖果的大小不等,這m個(gè)糖果的大小分別是s1,s2,s3,…,sm。除此之外,每個(gè)孩子對(duì)糖果大小的需求也是不一樣的,只有糖果的大小大于等于孩子的對(duì) 糖果大小的需求的時(shí)候,孩子才得到滿足。假設(shè)這n個(gè)孩子對(duì)糖果大小的需求分別是g1,g2,g3,…,gn。如何分配糖果,能盡可能滿足最多數(shù)量的孩子?

期望值:最多滿足孩子的個(gè)數(shù)
限制值:糖果大小s >= g ,才視為該糖果滿足該孩子。
滿足限制值的情況下,不論用小糖果還是大糖果滿足孩子,對(duì)期望值的貢獻(xiàn)都是一樣的,滿足的孩子個(gè)數(shù)++ 而已。

貪心算法:

  • 對(duì)于一個(gè)孩子,用較小的糖果能夠滿足,則沒(méi)有必要用更大的糖果;更大的糖果可以用來(lái)滿足更多需求的孩子。
  • 對(duì)于一個(gè)糖果,從較小需求的孩子開(kāi)始,越容易滿足。

代碼如下:

// simple greedy algorithm: distribute candies
// six childs' request: 1 1 3 2 2 8
// five candies size:   4 2 5 7 9
//
// Greedy algorithm is suitable to solve the problem.
// We can let the smaller candy's size to satisfy the 
// smaller resquest of child.
int distributeCandy(vector<int> candies,vector<int> childs) {if(candies.size() == 0 || childs.size() == 0) {return 0;}// sort, we can compare from small to big requestsort(candies.begin(), candies.end());sort(childs.begin(), childs.end());int res = 0;int i, j;for (i = 0, j = 0;i < childs.size() && j < candies.size(); i++,j++) {if (childs[i] <= candies[j]) {res ++;}}return res;
}

完整測(cè)試代碼:
https://github.com/BaronStack/DATA_STRUCTURE/blob/master/greedy/distribute_candies.cc

2.2 錢(qián)幣找零

這個(gè)問(wèn)題在我們的日常生活中更加普遍。假設(shè)我們有1元、2元、5元、10元、20元、50元、100元這些面額的紙幣,它們的張數(shù)分別是c1、c2、c5、c10、c20、c50、c100。我們現(xiàn)在要用這些錢(qián)來(lái)支付K元,最少要用多少?gòu)埣垘拍?
在生活中,我們肯定是先用面值最大的來(lái)支付,如果不夠,就繼續(xù)用更小一點(diǎn)面值的,以此類(lèi)推,最后剩下的用1元來(lái)補(bǔ)齊。

期望值:紙幣數(shù)目最少
限制值:每種面額的張數(shù)

在滿足限制值的情況下,希望用最少的紙幣數(shù)目達(dá)成期望值。不論使用面額大的紙幣還是面額小的紙幣,期望值的紙幣數(shù)目都會(huì)++,貢獻(xiàn)一樣。所以相同貢獻(xiàn)值的情況下,使用更大面額的紙幣更容易滿足期望值。

代碼如下:

int cmp(pair<int, int> a, pair<int, int> b) {return a.first > b.first;
}// Get the total num of papers that satisfy the K
// param1: nums of every coin
// param2: target number
// param3: cost of every coin in coins
void getCoinNums(vector<pair<int,int>> coins,int K, vector<pair<int,int>> &result) {if (coins.size() == 0) {return;}int i, tmp;// sort from biger to smallersort(coins.begin(), coins.end(), cmp);i = 0;tmp = 0;while(K && i < coins.size()) {tmp = K / coins[i].first;if (tmp != 0) {int real_nums;// defend the real coin nums overheadif (tmp <= coins[i].second) {real_nums = tmp;} else {real_nums = coins[i].second;}K -= real_nums * coins[i].first;result.push_back(make_pair(coins[i].first, real_nums));}i++;}
}

完整測(cè)試代碼:
https://github.com/BaronStack/DATA_STRUCTURE/blob/master/greedy/coins_charge.cc

2.3 最多覆蓋區(qū)間

假設(shè)我們有n個(gè)區(qū)間,區(qū)間的起始端點(diǎn)和結(jié)束端點(diǎn)分別是[l1, r1],[l2, r2],[l3, r3],…,[ln, rn]。我們從這n個(gè)區(qū)間中選出一部分區(qū)間,這部分區(qū)間滿足兩兩不相 交(端點(diǎn)相交的情況不算相交),最多能選出多少個(gè)區(qū)間呢?

比如區(qū)間: [6,8], [2,4], [3,5], [1,5], [5,9], [8,10], 則最多不相交區(qū)間為 : [2,4], [6,8], [8,10]

我們將各個(gè)區(qū)間的右端點(diǎn)從小到大排序,每次選擇區(qū)間時(shí)只需要確認(rèn)當(dāng)前區(qū)間的左端點(diǎn)比上一個(gè)區(qū)間的右端點(diǎn)大就可以了。之所以選擇對(duì)右端點(diǎn)進(jìn)行從小到大的排序,因?yàn)橛叶它c(diǎn)決定的是一個(gè)區(qū)間的下界,我們每次選擇盡可能選擇下界小且和之前的區(qū)間沒(méi)有相交的區(qū)間,則才能夠得到最多的不相交區(qū)間。

實(shí)現(xiàn)代碼如下:

int cmp(pair<int, int> a, pair<int, int> b) {if (a.second < b.second) {return 1;} else if (a.second == b.second &&a.first < b.first) {return 1;} else {return 0;}
}// algorithm:
// 1. Sort the array with right node increase
// 2. Maintain a num e, if rest of the interval's left node
// bigger than e, then the interval will be choosen
//
// example:
// Befor sort: [6,8], [2,4], [3,5], [1,5], [5,9], [8,10]
// After sort: [2,4], [1,5], [3,5], [6,8], [5,9], [8,10]
//
// result : [2,4], [6,8], [8,10]
void intervalCoverage(vector<pair<int,int>> intervals,vector<pair<int,int>> &result) {if (intervals.size() == 0) {return;}int i, e, count;sort(intervals.begin(), intervals.end(), cmp);e = -1;count = 0;for (i = 0;i < intervals.size(); i++) {if(intervals[i].first >= e) {count ++;e = intervals[i].second;result.push_back(make_pair(intervals[i].first,intervals[i].second));}}
}

完整測(cè)試代碼:
https://github.com/BaronStack/DATA_STRUCTURE/blob/master/greedy/interval_coverage.cc

2.4 哈夫曼編解碼

哈夫曼編碼是一種針對(duì)數(shù)據(jù)高效壓縮的編碼方式,能夠達(dá)到20%-90%的壓縮比。
比如:

  • 1000長(zhǎng)度的字符串 需要1000bytes = 8000 bit的存儲(chǔ)空間。
  • 優(yōu)化:如果1000長(zhǎng)度的字符串中總共有8個(gè)不同的字符,則這八個(gè)字符可以用三個(gè)bit位就能表示(000-111),1000長(zhǎng)度的字符串只需要3000 bit的存儲(chǔ)空間
  • 進(jìn)一步優(yōu)化:haffman編碼,每個(gè)字符的bit位表示可以不定長(zhǎng)(解碼會(huì)復(fù)雜一些),總長(zhǎng)度不會(huì)超過(guò)3位(1000字符不會(huì)超過(guò)3000bit的存儲(chǔ)空間)。haffman編碼為了滿足不定長(zhǎng)的要求,不會(huì)出現(xiàn)針對(duì)某一個(gè)字符的編碼是另一個(gè)字符編碼前綴的情況。

比如如下字符表,huffman編碼 以及 其對(duì)應(yīng)的總二進(jìn)制位數(shù) ,最后1000字符總共也只有2100的bit存儲(chǔ)空間。

所以這里根據(jù)頻率構(gòu)建huffman 編碼的過(guò)程就用到了貪心的思想。

構(gòu)建huffman樹(shù)的過(guò)程 選擇兩個(gè)小頻率的字符開(kāi)始構(gòu)建的父節(jié)點(diǎn)作為一個(gè)新節(jié)點(diǎn),再選擇一個(gè)次小的與該新節(jié)點(diǎn)一起構(gòu)建一個(gè)新的父節(jié)點(diǎn),依次由下向上構(gòu)建。最終出現(xiàn)頻率越高的字符串越靠近根節(jié)點(diǎn)。

構(gòu)建過(guò)程如下:

void huffmanTree(priority_queue<Node> &q) {// root node is store in proiority_queue// when the q size is 1while (q.size() != 1) {Node *left = new Node(q.top()); q.pop();Node *right = new Node(q.top()); q.pop();// father's node and it's left and right childNode node('R', left->frequency + right->frequency, left, right);q.push(node);}
}

構(gòu)建完huffman樹(shù)之后,進(jìn)行各個(gè)字符的編碼,這里僅僅將所有的左子樹(shù)權(quán)值設(shè)置為0,又子樹(shù)權(quán)值設(shè)置為1就可以了

huffman編碼過(guò)程如下:

// Huffman encode function
// param1: Root is the huffman tree's root node
// param2: prefix is the encode result per char
// param3: a map with 'char' and it's huffman's encode
void huffmanEncode(Node *root, string &prefix,map<char, string> &result) {string m_prefix = prefix;if (root->left == nullptr)return;// set the left weight recursionprefix += "0";if (root->left->left == nullptr) {// find the char's result in the leaf noderesult[root->left->c] = prefix;} else {huffmanEncode(root->left, prefix, result);}// back to the begin node to set the right weightprefix = m_prefix;prefix += "1";if (root->right->right == nullptr) {result[root->right->c] = prefix;} else {huffmanEncode(root->right, prefix, result);}
}

huffman解碼如下:

// Huffman decode function
// param1: des is the input string to be decode
// param2: res is the map between char and huffman's
// encode string
// param3: decode string
bool huffmanDecode(string des, map <char, string> res,string &result) {if (des == "") {return false;}int i;map<char,string>::const_iterator it;string buf_str = "";for (i = 0; i < des.size(); i ++) {buf_str += des[i];for (it = res.begin() ; it != res.end(); it++ ) {if (it->second == buf_str) {result += it->first; buf_str = "";break;}}if(i == des.size() - 1 && it == res.end()) {return false;}}return true;
}

完整測(cè)試代碼:
https://github.com/BaronStack/DATA_STRUCTURE/blob/master/greedy/haffman.cc

總結(jié)

以上是生活随笔為你收集整理的贪心算法简单实践 -- 分糖果、钱币找零、最多区间覆盖、哈夫曼编解码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。