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] 的边界相互“接触”,但没有相互重叠。

思路:

  1. 找移除区间的最小数量,意味着需要尽可能多的保留区间;
  2. 每个区间的范围越小,需要留下的区间就越多;
  3. 区间的开始已经由上个区间固定,所以决定区间范围是区间的结尾,区间结尾约小,需要的区间就越多;
  4. 因此可以按区间的结尾进行排序,每次选择结尾最小,并且和前一个区间不重叠的区间。
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