貪心算法是指:在每一步求解的步驟中,它要求「貪婪」的選擇最佳操做,並但願經過一系列的最優選擇,可以產生一個問題的(全局的)最優解。java
貪心算法每一步必須知足一下條件:算法
一、可行的:即它必須知足問題的約束。數組
二、局部最優:他是當前步驟中全部可行選擇中最佳的局部選擇。ide
三、不可取消:即選擇一旦作出,在算法的後面步驟就不可改變了。this
1.活動選擇問題
這是《算法導論》上的例子,也是一個很是經典的問題。有n個須要在同一天使用同一個教室的活動a1,a2,…,an,教室同一時刻只能由一個活動使用。每一個活動ai都有一個開始時間si和結束時間fi 。一旦被選擇後,活動ai就佔據半開時間區間[si,fi)。若是[si,fi]和[sj,fj]互不重疊,ai和aj兩個活動就能夠被安排在這一天。該問題就是要安排這些活動使得儘可能多的活動能不衝突的舉行。例以下圖所示的活動集合S,其中各項活動按照結束時間單調遞增排序。spa
用貪心法的話思想很簡單:活動越早結束,剩餘的時間是否是越多?那我就早最先結束的那個活動,找到後在剩下的活動中再找最先結束的不就得了?code
雖然貪心算法的思想簡單,可是貪心法不保證能獲得問題的最優解,若是得不到最優解,那就不是咱們想要的東西了,因此咱們如今要證實的是在這個問題中,用貪心法能獲得最優解。blog
java代碼實現:排序
1 public class ActiveTime { 2 public static void main(String[] args) { 3 //建立活動並添加到集合中 4 Active act1 = new Active(1, 4); 5 Active act2 = new Active(3, 5); 6 Active act3 = new Active(0, 6); 7 Active act4 = new Active(5, 7); 8 Active act5 = new Active(3, 8); 9 Active act6 = new Active(5, 9); 10 Active act7 = new Active(6, 10); 11 Active act8 = new Active(8, 11); 12 Active act9 = new Active(8, 12); 13 Active act10 = new Active(2, 13); 14 Active act11 = new Active(12, 14); 15 List<Active> actives = new ArrayList<Active>(); 16 actives.add(act1); 17 actives.add(act2); 18 actives.add(act3); 19 actives.add(act4); 20 actives.add(act5); 21 actives.add(act6); 22 actives.add(act7); 23 actives.add(act8); 24 actives.add(act9); 25 actives.add(act10); 26 actives.add(act11); 27 28 List<Active> bestActives = getBestActives(actives, 0, 16); 29 for (int i = 0; i < bestActives.size(); i++) { 30 System.out.println(bestActives.get(i)); 31 } 32 } 33 34 35 /** 36 * 37 * @param actives 38 * 活動集合 39 * @param startTime 40 * 教室的開始使用時間 41 * @param endTime 42 * 教室的結束使用時間 43 * @return 44 */ 45 public static List<Active> getBestActives(List<Active> actives, int startTime, int endTime) { 46 //最佳活動選擇集合 47 List<Active> bestActives = new ArrayList<Active>(); 48 //將活動按照最先結束時間排序 49 actives.sort(null); 50 //nowTime 用來記錄上次活動結束時間 51 int nowTime = startTime; 52 /** 53 * 由於咱們已經按照最先結束時間排序,那麼只要活動在時間範圍內 54 * actives.get(1)就應當是第一個活動的結束時間. 55 * 則咱們記錄第一次活動結束的時間,在結合剩下的活動中, 56 * 選取開始時間大於nowTime且結束時間又在範圍內的活動,則爲第二次活動時間, 57 * 知道選出全部活動 58 */ 59 for (int i = 0; i < actives.size(); i++) { 60 Active act = actives.get(i); 61 if(act.getStartTime()>=nowTime&&act.getEndTime()<=endTime){ 62 bestActives.add(act); 63 nowTime = act.getEndTime(); 64 } 65 } 66 return bestActives; 67 } 68 } 69 70 /** 71 * 活動類 72 * @CreatTime 下午9:45:37 73 * 74 */ 75 class Active implements Comparable<Active>{ 76 private int startTime;//活動開始時間 77 private int endTime;//活動結束時間 78 79 public Active(int startTime, int endTime) { 80 super(); 81 this.startTime = startTime; 82 this.endTime = endTime; 83 } 84 85 public int getStartTime() { 86 return startTime; 87 } 88 89 public void setStartTime(int startTime) { 90 this.startTime = startTime; 91 } 92 93 public int getEndTime() { 94 return endTime; 95 } 96 97 public void setEndTime(int endTime) { 98 this.endTime = endTime; 99 } 100 101 @Override 102 public String toString() { 103 return "Active [startTime=" + startTime + ", endTime=" + endTime + "]"; 104 } 105 106 //活動排序時按照結束時間升序 107 @Override 108 public int compareTo(Active o) { 109 if(this.endTime>o.getEndTime()){ 110 return 1; 111 }else if(this.endTime == o.endTime){ 112 return 0; 113 }else{ 114 return -1; 115 } 116 } 117 118 119 }
運行結果:get
Active [startTime=1, endTime=4] Active [startTime=5, endTime=7] Active [startTime=8, endTime=11] Active [startTime=12, endTime=14]
能夠看出,求得的結果正好是最優解。
2.錢幣找零問題
這個問題在咱們的平常生活中就更加廣泛了。假設1元、2元、5元、10元、20元、50元、100元的紙幣分別有c0, c1, c2, c3, c4, c5, c6張。如今要用這些錢來支付K元,至少要用多少張紙幣?用貪心算法的思想,很顯然,每一步儘量用面值大的紙幣便可。在平常生活中咱們天然而然也是這麼作的。在程序中已經事先將Value按照從小到大的順序排好。
java代碼實現:
1 package GreedyAlgorithm; 2 3 public class CoinChange { 4 public static void main(String[] args) { 5 //人民幣面值集合 6 int[] values = { 1, 2, 5, 10, 20, 50, 100 }; 7 //各類面值對應數量集合 8 int[] counts = { 3, 1, 2, 1, 1, 3, 5 }; 9 //求442元人民幣需各類面值多少張 10 int[] num = change(442, values, counts); 11 print(num, values); 12 } 13 14 public static int[] change(int money, int[] values, int[] counts) { 15 //用來記錄須要的各類面值張數 16 int[] result = new int[values.length]; 17 18 for (int i = values.length - 1; i >= 0; i--) { 19 int num = 0; 20 //須要最大面值人民幣張數 21 int c = min(money / values[i], counts[i]); 22 //剩下錢數 23 money = money - c * values[i]; 24 //將須要最大面值人民幣張數存入數組 25 num += c; 26 result[i] = num; 27 } 28 return result; 29 } 30 31 /** 32 * 返回最小值 33 */ 34 private static int min(int i, int j) { 35 return i > j ? j : i; 36 } 37 38 private static void print(int[] num, int[] values) { 39 for (int i = 0; i < values.length; i++) { 40 if (num[i] != 0) { 41 System.out.println("須要面額爲" + values[i] + "的人民幣" + num[i] + "張"); 42 } 43 } 44 } 45 }
運行結果:
須要面額爲2的人民幣1張
須要面額爲5的人民幣2張
須要面額爲10的人民幣1張
須要面額爲20的人民幣1張
須要面額爲100的人民幣4張
能夠看出,求出的結果也恰好等於442元。正好爲最優解。可是,當面額及數量爲下種特殊狀況時,貪心算法就沒法給出最優解。
//人民幣面值集合
6 int[] values = { 3, 5, 10, 20, 50, 100 };
7 //各類面值對應數量集合
8 int[] counts = { 3, 2, 1, 1, 3, 5 };
須要求得money = 416元
運行結果以下:
須要面額爲5的人民幣1張
須要面額爲10的人民幣1張
須要面額爲100的人民幣4張
因而咱們能夠看出,有些狀況,貪心算法確實能夠給出最優解,然而,還有一些問題並非這種狀況。對於這種狀況,咱們關心的是近似解,或者只能知足於近似解,貪心算法也是有價值的。