貪心算法解決問題的步驟
- 當我們看到這類問題的時候,首先要聯想到貪心算法:針對一組數據,我們定義了它的限制值和期望值,希望從中選出幾個數據,在滿足限制值的條件下,期望值最大。
- 我們嘗試下這個問題是否可以用貪心算法解決: 每次選擇當前情況下,在對限制值同等貢獻量的情況下,對期望值貢獻最大的數據。
- 舉幾個例子代入問題,看看產生的結果是否是最優: 大部分情況下,舉個例子驗證下就可以了。嚴格地證明貪心算法的正確性,是非常復雜的,需要涉及比較多的數學推理。而且,從實踐的角度來說,大部分能用貪心算法解決的問題,貪心算法的正確性都是顯而易見的,也不需要嚴格的數學的推導證明。
學習貪心算法
關鍵是多實踐。在使用中找到感覺。
1. 分糖果問題
我們有 m 個糖果和 n 個孩子,我們現在要把糖果分給這些孩子吃,但是糖果少,孩子多 (m < n), 所以糖果只能分給一部分孩子。
每個糖果的大小不一樣,分別是 s1, s2, s3, ……, sm。除此之外,每個孩子對糖果的需求也是不一樣的,只有糖果大小大于或者等于孩子對糖果大小的需求的時候,孩子才能得到滿足。假設這 n 個孩子對糖果大小的需求分別是 g1, g2, g3, ……, gn。
問題是,如何分配糖果,盡可能滿足最多數量的孩子。
對問題分析,限制值是這堆糖果,貢獻值是得到滿足的孩子。嘗試貪心算法,如果對一個孩子來說,一個小的糖果可以滿足,就絕不用更大的糖果來滿足他。
設計算法:對孩子按照需求從小到大排序,對糖果按照大小從小到大排序,遍歷孩子,用最小的糖果滿足當前的孩子,直到糖果用完或者孩子遍歷完。
2. 錢幣找零
假設我們有 1 元、2 元、 5 元、10 元、20 元、50 元、100 元這些面額的紙幣,它們的張數分別是 c1、c2、c5、c10、c20、c50、c100。現在我們要用這些錢來支付 K 元,最少要用多少張紙幣呢?
分析問題:限制值是紙幣的張數,希望越少越少,貢獻值是紙幣的總面額,要達到 K 元。
設計貪心算法:每次從剩余紙幣中選擇單張對總面額貢獻率最大且不會使得總面額超過 K 元的紙幣。
另外要注意的是,若是找到最后 c1 的張數不夠的話,就要使用動態規劃算法了。
3. 區間覆蓋
假設我們有 n 個區間,區間的起始端點和結束端點分別是 [l1, r1], [l2, r2], [l3, r3], ……, [ln, rn]。我們從這 n 區間中選出一部分區間,這部分區間滿足兩兩不相交(端點相交的情況不算相交),最多能選出多少個區間呢?
假設這 n 個區間的最左端點是 lmin,最右端點是 rmax。這個問題就相當于,在總可用區間 rmax - lmin 中,從左到右,每次消耗最少的空間,增加一個覆蓋區間。而這個消耗最少空間,計算公式是 左端點下標+區間長度。
4. 哈夫曼編碼
哈夫曼編碼,是一種壓縮算法,壓縮率通常在 20% ~ 90% 之間。它的算法思想是通過用不等長的編碼來表示每個字符,使得最終文本占用的存儲空間更少。
實現哈夫曼編碼的過程是,對所有字符輔帶著頻率放在優先級隊列中,我們從隊列中取出頻率最小的兩個節點 A、B,新建一個節點 C 作為 AB 兩個節點的父節點,頻率設置為這兩個節點的頻率之和。最后把 C 節點放入優先級隊列中。重復這個過程,直到隊列中沒有數據。最后得到一棵樹,我們給這棵樹每條邊畫上一個權值,指向左子樹的邊標記為 0 ,指向右子樹的邊標記為 1。這樣下來,從根節點到葉節點的路徑就是葉節點字符對應的哈夫曼編碼。