重溫算法Day18:貪心算法

分糖果
我們有 m 個糖果和 n 個孩子。我們現在要把糖果分給這些孩子吃,但是糖果少,(m<n),所以糖果只能分配給一部分孩子。
每個糖果的大小不等,這 m 個糖果的大小分別是 s1,s2,s3,……,sm。除此之外,每個孩子對糖果大小的需求也是不一樣的,只有糖果的大小大於等於孩子的對糖果大小的需求的時候,孩子纔得到滿足。
假設這 n 個孩子對糖果大小的需求分別是 g1,g2,g3,……,gn。我的問題是,如何分配糖果,能儘可能滿足最多數量的孩子?

對於一個孩子來說,如果小的糖果可以滿足,我們就沒必要用更大的糖果,這樣更大的就可以留給其他對糖果大小需求更大的孩子。另一方面,對糖果大小需求小的孩子更容易被滿足,所以,我們可以從需求小的孩子開始分配糖果。因爲滿足一個需求大的孩子跟滿足一個需求小的孩子,對我們期望值的貢獻是一樣的。

我們每次從剩下的孩子中,找出對糖果大小需求最小的,然後發給他剩下的糖果中能滿足他的最小的糖果,這樣得到的分配方案,也就是滿足的孩子個數最多的方案。


錢幣找零
這個問題在我們的日常生活中更加普遍。假設我們有 1 元、2 元、5 元、10 元、20 元、50 元、100 元這些面額的紙幣,它們的張數分別是 c1、c2、c5、c10、c20、c50、c100。我們現在要用這些錢來支付 K 元,最少要用多少張紙幣呢?

在生活中,我們肯定是先用面值最大的來支付,如果不夠,就繼續用更小一點面值的,以此類推,最後剩下的用 1 元來補齊。


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

我們假設這 n 個區間中最左端點是 lmin,最右端點是 rmax。這個問題就相當於,我們選擇幾個不相交的區間,從左到右將[lmin, rmax]覆蓋上。
我們按照起始端點從小到大的順序對這 n 個區間排序。
我們每次選擇的時候,左端點跟前面的已經覆蓋的區間不重合的,右端點又儘量小的,這樣可以讓剩下的未覆蓋區間儘可能的大,就可以放置更多的區間。


霍夫曼編碼

是一種十分有效的編碼方法,廣泛用於數據壓縮中
假設我有一個包含 1000 個字符的文件,每個字符佔 1 個 byte(1byte=8bits),存儲這 1000 個字符就一共需要 8000bits,那有沒有更加節省空間的存儲方式呢?


假設我們通過統計分析發現,這 1000 個字符中只包含 6 種不同字符,假設它們分別是 a、b、c、d、e、f。
而 3 個二進制位(bit)就可以表示 8 個不同的字符,所以,爲了儘量減少存儲空間,每個字符我們用 3 個二進制位來表示。
那存儲這 1000 個字符只需要 3000bits 就可以了,比原來的存儲方式節省了很多空間。
a(000)、b(001)、c(010)、d(011)、e(100)、f(101)

霍夫曼編碼不僅會考察文本中有多少個不同字符,還會考察每個字符出現的頻率,根據頻率的不同,選擇不同長度的編碼。
霍夫曼編碼試圖用這種不等長的編碼方法,來進一步增加壓縮的效率。如何給不同頻率的字符選擇不同長度的編碼呢?
根據貪心的思想,我們可以把出現頻率比較多的字符,用稍微短一些的編碼;出現頻率比較少的字符,用稍微長一些的編碼。

對於等長的編碼來說,我們解壓縮起來很簡單。比如剛纔那個例子中,我們用 3 個 bit 表示一個字符。在解壓縮的時候,我們每次從文本中讀取 3 位二進制碼,然後翻譯成對應的字符。
但是,霍夫曼編碼是不等長的,每次應該讀取 1 位還是 2 位、3 位等等來解壓縮呢?這個問題就導致霍夫曼編碼解壓縮起來比較複雜。爲了避免解壓縮過程中的歧義,霍夫曼編碼要求各個字符的編碼之間,不會出現某個編碼是另一個編碼前綴的情況。

假設這 6 個字符出現的頻率從高到低依次是 a、b、c、d、e、f。我們把它們編碼下面這個樣子,任何一個字符的編碼都不是另一個的前綴,在解壓縮的時候,我們每次會讀取儘可能長的可解壓的二進制串,所以在解壓縮的時候也不會歧義。經過這種編碼壓縮之後,這 1000 個字符只需要 2100bits 就可以了。

編碼技巧
我們把每個字符看作一個節點,並且輔帶着把頻率放到優先級隊列中。我們從隊列中取出頻率最小的兩個節點 A、B,然後新建一個節點 C,把頻率設置爲兩個節點的頻率之和,並把這個新節點 C 作爲節點 A、B 的父節點。最後再把 C 節點放入到優先級隊列中。重複這個過程,直到隊列中沒有數據。

我們給每一條邊加上畫一個權值,指向左子節點的邊我們統統標記爲 0,指向右子節點的邊,我們統統標記爲 1,那從根節點到葉節點的路徑就是葉節點對應字符的霍夫曼編碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章