LeetCode-贪心算法问题整理

  |   0 评论   |   0 浏览

今天,我们先来学习一下贪心算法(greedy algorithm)。贪心算法有很多经典的应用,比如霍夫曼编码(Huffman Coding)、Prim和Kruskal最小生成树算法、还有Dijkstra单源最短路径算法。

1.买卖股票的最佳时机

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:

输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。


解题思路:

func maxProfit(prices []int) int {
	// 1. 暴力 双重循环
	//max := 0
	//length := len(prices)
	//for i := 0; i < length - 1; i++ {
	//	j := i +1
	//
	//	for j < length && prices[i] < prices[j] {
	//		temp := prices[j] - prices[i]
	//		if temp > max {
	//			max = temp
	//		}
	//		j++
	//	}
	//}
	//return max
	// 2. 贪心算法,单层循环
	min := math.MaxInt32
	max := 0
	for _, price := range prices {
		if price < min {
			min = price
		}

		temp := price - min
		if temp > max {
			max = temp
		}
	}
	return max
}

2.分发糖果

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:

每个孩子至少分配到 1 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?

示例 1:

输入: [1,0,2]
输出: 5
解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。
示例 2:

输入: [1,2,2]
输出: 4
解释: 你可以分别给这三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这已满足上述两个条件。

解题思路:通过正反两次遍历解决,GO实现代码如下:

func candy(ratings []int) int {

	length := len(ratings)
	var candies [] int

	// 正序
	for k, v := range ratings {
		candies = append(candies, 1)

		if k > 0 && v > ratings[k-1] {
			candies[k] = candies[k-1] + 1
		}
	}

	// 倒序
	for j := length - 2; j >= 0; j-- {
		if ratings[j] > ratings[j+1] && candies[j] <= candies[j+1] {
			candies[j] = candies[j+1] + 1
		}
	}

	var count int

	for _, v := range candies {
		count += v
	}
	return count
}

3.判断子序列

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。

示例 1:
s = "abc", t = "ahbgdc"

返回 true.

示例 2:
s = "axc", t = "ahbgdc"

返回 false.

解题思路:双重循环去遍历,外层循环是子序列T,内层循环是字符串S,从S中找到T的第一个字符后j++判断下一个位置,通过计数器累加,最终判断计数器是否等于T的长度

func isSubsequence(s string, t string) bool {

	sMatchLen := 0
	j := 0
	for i := 0; i < len(s); i++ {

		for  j < len(t) {
			if s[i] == t[j] {
				sMatchLen++
				j++
				break
			}
			j++
		}
	}

	return sMatchLen == len(s)
}

4.分发饼干

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

 
示例 1:

输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。
示例 2:

输入: g = [1,2], s = [1,2,3]
输出: 2
解释:
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.

解题思路:先对两个数组排序。然后双重循环解决,外层循环是糖果S,内层循环是孩子G,G[j] <= S[i] 则能发成功一个糖果

func findContentChildren(g []int, s []int) int {
	if len(g) < 0 || len(s) < 0 {
		return 0
	}

	count := 0

	sort.Ints(g)
	sort.Ints(s)
	j:=0
	for i:=0; i<len(g); i++ {

		for j<len(s)  {
			if g[i] <= s[j]  {
				count ++
				j++
				break
			}

			j++
		}
	}

	return count
}

func  contails(target[] int, t int ) bool  {

	for _, v := range target {
		if v == t {
			return true
		}
	}
	return false
}


标题:LeetCode-贪心算法问题整理
作者:guobing
地址:http://www.guobingwei.tech/articles/2020/09/23/1600842063562.html