leetcode刷题类型(四)- - - 贪心思想
保证每次操作都是局部最优的,并且最后得到的结果是全局最优的。
经典题目
455 分发饼干
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
思路:
- 给一个孩子的饼干应当尽量小并且又能满足该孩子,这样大饼干才能拿来给满足度比较大的孩子。
- 因为满足度最小的孩子最容易得到满足,所以先满足满足度最小的孩子。
func findContentChildren(g []int, s []int) int {
if len(s)==0{
return 0 //没有饼干,一个人也满足不了
}
sort.Ints(g) //先进行原地排序
sort.Ints(s) //先进行原地排序
i:=0
j:=0
for i<len(g) && j<len(s){
if g[i] <= s[j] { //第i个小孩被满足,只有第i个小孩被满足了,才会尝试满足第i+1个
i++
}
j++
}
return i
}
435 无重叠区间
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
- 可以认为区间的终点总是大于它的起点。
- 区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
思路:
- 找移除区间的最小数量,意味着需要尽可能多的保留区间;
- 每个区间的范围越小,需要留下的区间就越多;
- 区间的开始已经由上个区间固定,所以决定区间范围是区间的结尾,区间结尾约小,需要的区间就越多;
- 因此可以按区间的结尾进行排序,每次选择结尾最小,并且和前一个区间不重叠的区间。
func eraseOverlapIntervals(intervals [][]int) int {
if len(intervals) == 0 {
return 0
}
sort.Slice(intervals, func(i, j int) bool { //根据区间结尾进行排序
return intervals[i][1] < intervals[j][1]
})
end := intervals[0][1] //第一个区间结尾,即排序后第一个元素的第二列
count := 1 //count记录保留的区间数,第一个区间为intervals[0]
for i := 1; i < len(intervals); i++ { //i=0是第一个区间,count已经计数
if intervals[i][0] < end { //下个区间的开始小于此时区间的结尾,产生了重叠
continue
}
end = intervals[i][1] //下个区间的开始在此时区间的结尾之后,没有重叠,再从下个区间的结尾开始往后找
count++
}
return len(intervals) - count
}
452 用最少数量的箭引爆气球
一支弓箭可以沿着 x 轴从不同点完全垂直地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。
给你一个数组 points ,其中 points [i] = [xstart,xend] ,返回引爆所有气球所必须射出的最小弓箭数。
思路:
沿x轴向右移动箭,使得移动前原本引爆的气球仍然被引爆,那么我们射出的箭就会更少,即每一支箭的射出位置都恰好对应着某一个气球的右边界。
考虑所有气球中右边界位置最靠左的那一个,那么一定有一支箭的射出位置就是它的右边界(否则就没有箭可以将其引爆了)。当我们确定了一支箭之后,我们就可以将这支箭引爆的所有气球移除,并从剩下未被引爆的气球中,再选择右边界位置最靠左的那一个,确定下一支箭,直到所有的气球都被引爆。
这题是 435 计算不重叠的区间个数 的变形,但[1, 2] 和 [2, 3] 算是重叠区间,此时箭到x=2时,两个气球都会爆炸
func findMinArrowShots(points [][]int) int {
if len(points) == 0 {
return 0
}
sort.Slice(points, func(i, j int) bool {
return points[i][1] < points[j][1]
})
maxRight := points[0][1] //所有气球中右边界位置最靠左的那一个,一定有一箭射中它的右边界
count := 1 //第一箭射中maxRight位置
//points[i][0](左坐标)等于maxRight时,被射向maxRight的箭射爆
//排序后points[i][0](左坐标)小于maxRight时,points[i][1] (右坐标)大于maxRight,也被射向maxRight的箭射爆
for i := 1; i < len(points); i++ {
if points[i][0] > maxRight {
maxRight = points[i][1]
count++
}
}
return count
}
121 买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
思路:
只要记录前面的最小价格,将这个最小价格作为买入价格,然后将当前的价格作为售出价格,查看当前收益是不是最大收益。
func maxProfit(prices []int) int {
if len(prices) == 0 {
return 0
}
minPrice := prices[0] //买入价格(最低价格)
profit := 0
for i := 1; i < len(prices); i++ {
if prices[i] < minPrice { //如果prices[i]的价格更低,那么改成在i时买入
minPrice = prices[i]
} else {
nowProfit := prices[i] - minPrice //此时可以卖出,卖出的收益为prices[i]-minPrice
if nowProfit > profit {
profit = nowProfit //如果这时卖出收益更多,那么改成在i时卖出
}
}
}
return profit
}
122 买卖股票的最佳时机 II
对于 [a, b, c, d],如果有 a <= b <= c <= d ,那么最大收益为 d - a。而 d - a = (d - c) + (c - b) + (b - a) ,因此当访问到一个 prices[i] 且 prices[i] - prices[i-1] > 0,那么就把 prices[i] - prices[i-1] 添加到收益中。
即一旦能挣钱就买,一旦亏钱了就卖,只要第二天不亏钱就不卖出。
func maxProfit(prices []int) int {
if len(prices) == 0 {
return 0
}
profit := 0
for i := 1; i < len(prices); i++ {
if prices[i]-prices[i-1] > 0 {
profit = profit + prices[i] - prices[i-1]
}
}
return profit
}
如有不对,烦请指出,感谢~
参考自:
http://www.cyc2018.xyz/
https://leetcode-cn.com/problems