介紹
貪心算法是在每一步選擇中都採取當前最好的或者最優的選擇,從而導致最終的結果是最好的或者最優的。貪心算法可以解決解決一些最優化問題,如求圖中的最小生成樹,求哈夫曼編碼。算法的思想還是比較容易理解的,難的是問題能否用貪心解決,貪心的具體策略是什麼?
翠花,上題。練過幾個題後估計你就會有自己的評估了
分發餅乾
題目來源:LeetCode 455.分發餅乾
題目描述:假設你是一位很棒的家長,想要給你的孩子們一些小餅乾。但是,每個孩子最多隻能給一塊餅乾。對每個孩子 i ,都有一個胃口值 gi ,這是能讓孩子們滿足胃口的餅乾的最小尺寸;並且每塊餅乾 j ,都有一個尺寸 sj 。如果 sj >= gi ,我們可以將這個餅乾 j 分配給孩子 i ,這個孩子會得到滿足。你的目標是儘可能滿足越多數量的孩子,並輸出這個最大數值。
注意:
你可以假設胃口值爲正。
一個小朋友最多隻能擁有一塊餅乾。
示例 1:
輸入: [1,2,3], [1,1]
輸出: 1
解釋:
你有三個孩子和兩塊小餅乾,3個孩子的胃口值分別是:1,2,3。
雖然你有兩塊小餅乾,由於他們的尺寸都是1,你只能讓胃口值是1的孩子滿足。
所以你應該輸出1。
示例 2:
輸入: [1,2], [1,2,3]
輸出: 2
解釋:
你有兩個孩子和三塊小餅乾,2個孩子的胃口值分別是1,2。
你擁有的餅乾數量和尺寸都足以讓所有孩子滿足。
所以你應該輸出2.
題目解析:現實生活中很容易遇到這種問題哈,想想你會怎麼做,讓小孩排隊,同時將並餅乾從小到大放好,儘量用最小的餅乾滿足每個小孩,同時又能讓他們喫飽,所以這就是貪心的策略了
public class Solution {
public int findContentChildren(int[] g, int[] s) {
if (g.length == 0 || s.length == 0) {
return 0;
}
Arrays.sort(g);
Arrays.sort(s);
int gIndex = 0;
int sIndex = 0;
while (gIndex < g.length && sIndex < s.length) {
if (g[gIndex] <= s[sIndex]) {
// g[gIndex]這個小孩獲得餅乾
gIndex++;
}
sIndex++;
}
return gIndex;
}
}
無重疊區間
題目來源:LeetCode 455.無重疊區間
題目描述:給定一個區間的集合,找到需要移除區間的最小數量,使剩餘區間互不重疊。
注意:
可以認爲區間的終點總是大於它的起點。
區間 [1,2] 和 [2,3] 的邊界相互“接觸”,但沒有相互重疊。
示例 1:
輸入: [ [1,2], [2,3], [3,4], [1,3] ]
輸出: 1
解釋: 移除 [1,3] 後,剩下的區間沒有重疊。
示例 2:
輸入: [ [1,2], [1,2], [1,2] ]
輸出: 2
解釋: 你需要移除兩個 [1,2] 來使剩下的區間沒有重疊。
示例 3:
輸入: [ [1,2], [2,3] ]
輸出: 0
解釋: 你不需要移除任何區間,因爲它們已經是無重疊的了。
題目解析:這個應該是最經典的貪心題了,這個題能變換出好多問法,例如給出一天內的課程,課程包括開始時間和結束時間,問一天最多能上多少節課。
其實這個每次選結束時間最早的課就能達到最優的效果。如果每次選開始時間最早的課能達到最優的效果嗎?
看下圖,如果每次選擇開始時間最早的課,選擇的課程爲1,3
如果每次選擇結束時間最早的課,選擇的課程爲1,4,5
public class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
if (intervals.length == 0) {
return 0;
}
// 按照每個數組的第二個元素升序排序
Arrays.sort(intervals, (int[] o1 ,int[] o2) -> (o1[1] - o2[1]));
int end = intervals[0][1];
int sum = 1;
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] >= end) {
sum++;
end = intervals[i][1];
}
}
return intervals.length - sum;
}
}
用最少數量的箭引爆氣球
題目地址:LeetCode 452. 用最少數量的箭引爆氣球
題目描述:在二維空間中有許多球形的氣球。對於每個氣球,提供的輸入是水平方向上,氣球直徑的開始和結束座標。由於它是水平的,所以y座標並不重要,因此只要知道開始和結束的x座標就足夠了。開始座標總是小於結束座標。平面內最多存在104個氣球。
一支弓箭可以沿着x軸從不同點完全垂直地射出。在座標x處射出一支箭,若有一個氣球的直徑的開始和結束座標爲 xstart,xend, 且滿足 xstart ≤ x ≤ xend,則該氣球會被引爆。可以射出的弓箭的數量沒有限制。 弓箭一旦被射出之後,可以無限地前進。我們想找到使得所有氣球全部被引爆,所需的弓箭的最小數量。
Example:
輸入:
[[10,16], [2,8], [1,6], [7,12]]
輸出:
2
解釋:
對於該樣例,我們可以在x = 6(射爆[2,8],[1,6]兩個氣球)和 x = 11(射爆另外兩個氣球)。
題目解析:這個題和上面的題基本上一樣,本質上就是計算最多的不重疊區間的個數,和上一題唯一的不同[1,2]和[2,3]算一個重疊空間。出題人爲了不讓刷題感到枯燥,變着法的改變問法。
public class Solution {
public int findMinArrowShots(int[][] points) {
if (points.length == 0) {
return 0;
}
Arrays.sort(points, (int[] o1 ,int[] o2) -> (o1[1] - o2[1]));
int end = points[0][1];
int sum = 1;
for (int i = 1; i < points.length; i++) {
if (points[i][0] > end) {
sum++;
end = points[i][1];
}
}
return sum;
}
}
種花問題
題目地址:LeetCode 605. 種花問題
題目描述:假設你有一個很長的花壇,一部分地塊種植了花,另一部分卻沒有。可是,花卉不能種植在相鄰的地塊上,它們會爭奪水源,兩者都會死去。
給定一個花壇(表示爲一個數組包含0和1,其中0表示沒種植花,1表示種植了花),和一個數 n 。能否在不打破種植規則的情況下種入 n 朵花?能則返回True,不能則返回False。
示例 1:
輸入: flowerbed = [1,0,0,0,1], n = 1
輸出: True
示例 2:
輸入: flowerbed = [1,0,0,0,1], n = 2
輸出: False
題目解析:從左到右發現能種一直種就行。
public class Solution {
public boolean canPlaceFlowers(int[] flowerbed, int n) {
int sum = 0;
for (int i = 0; i < flowerbed.length && sum < n; i++) {
if (flowerbed[i] == 0) {
int pre = i == 0 ? 0 : flowerbed[i - 1];
int after = i == flowerbed.length - 1 ? 0 : flowerbed[i + 1];
if (pre + after == 0) {
flowerbed[i] = 1;
sum++;
}
}
}
return sum == n;
}
}
後記
最後留一道思考題,你覺得下面的題可以用貪心解決嗎?如果解決不了,該用什麼算法解決呢?
歡迎關注
參考博客
[1]https://blog.csdn.net/qq_43699339/article/details/90146817