代码随想录:贪心算法

455. 分发饼干

解题步骤

  1. 排序:给两个数组排序
  2. 举例判断:看第一个情况不满足,要换孩子还是换物品?
    a. 换孩子→遍历孩子
    b. 换物品→遍历物品

实战:

  1. 选择从小到大排序
  2. 第一个不满足,我要换物品。因为换了孩子还是不满足。
  3. 确定了遍历顺序:换物品→遍历物品

go实现

/**
 * @date: 2024 Jan 06
 * @time: 10:50
 * @author: Chris
 * @title: 455. Assign Cookies
 * @link: https://leetcode.cn/problems/assign-cookies/description/
**/
package day31

import (
	"sort"
)

// 优先满足胃口小的孩子,
// 如果不可以换下一个物品;如果换孩子的的话也不能满足
func findContentChildren(g []int, s []int) int {
	// sort by ascending
	sort.Ints(g)
	sort.Ints(s)
	child := 0
	// 遍历物品,如果不行换下个物品
	for sIndex := 0; child < len(g) && sIndex < len(s); sIndex++ {
		// 物品sIndex在不断递增
		// child只有满足的当前孩子时候才递增,才会尝试分配给下一个孩子
		if s[sIndex] >= g[child] {
			child++
		}
	}
	return child
}

// 优先满足胃口大的孩子,
// 如果不行,换下个孩子; 如果换下一个物品的话也不能满足。
func findContentChildren2(g []int, s []int) int {
	// sort
	sort.Ints(g)
	sort.Ints(s)

	sIndex := len(s) - 1
	// 遍历孩子,如果不行换孩子
	for i := len(g) - 1; sIndex >= 0 && i >= 0; i-- {
		// 孩子可能大于饼干数,注意sIndex别越界
		if  s[sIndex] >= g[i] {
			sIndex--
		}
	}
	return (len(s) - 1) - sIndex
}

55. 跳跃游戏

/**
 * @date: 2024 Jan 06
 * @time: 13:32
 * @author: Chris
 * @title: 55. Jump Game
 * @link: https://leetcode.cn/problems/jump-game/description/
**/
package day32

// 跳跃覆盖范围究竟可不可以覆盖到终点!
func canJump(nums []int) bool {
	if len(nums) == 1 {
		return true
	}
	// 记录 能到达的最大的位置
	cover := 0

	// 注意这里i的范围是cover,是能到达的距离!超过这个距离不能到达
	for i := 0; i <= cover; i++ {
		// 更新cover = (当前位置+跳跃距离, 旧cover)
		cover = max(i+nums[i], cover)
		if cover >= len(nums)-1 {
			return true
		}
	}
	return false
}

45. 跳跃游戏 II

思路:

记录步骤的规则: 每超过上一次可达最大范围,需要跳跃一次。
所以 在i==上一次可达范围+1的位置进行记录,次数+1
if (i == lastDistance + 1) minSetp++;

从题目中可知:一开始到达了nums[0]位置,从nums[1]记录一次!
然后题目确保了可到达尾巴nums[n-1]。

/**
 * @date: 2024 Jan 06
 * @time: 13:44
 * @author: Chris
 * @title: 45. Jump Game II
 * @link: https://leetcode.cn/problems/jump-game-ii/description/
**/
package day32

// 记录步骤规则:每超过上一次可达最大范围,需要跳跃一次,次数+1
// 记录位置:i == lastDistance + 1
func jump(nums []int) int {
	// 根据题目规则,初始位置为nums[0]
	lastDistance := 0 // 上一次覆盖范围
	curDistance := 0  // 当前覆盖范围(可达最大范围)
	minStep := 0      // 记录最少跳跃次数

	for i := 0; i < len(nums); i++ {
		if i == lastDistance+1 { // 在上一次可达范围+1的位置,记录步骤
			minStep++                  // 跳跃次数+1
			lastDistance = curDistance // 记录时才可以更新
		}
		curDistance = max(nums[i]+i, curDistance) // 更新当前可达的最大范围
	}
	return minStep
}

135. 分发糖果

1. 初始化每人一个糖果

2. 确保每个比左邻居评分高的孩子,比左邻居多一颗糖果。
a. 从左向右遍历–>,和右邻居比较

💡 因为判断是基于左邻居,应先确定左边,所以从左到右遍历

3. 确保每个比右邻居评分高的孩子,比右邻居多一颗糖果。
a. 从右向左遍历<–,和右邻居比较

💡 判断基于右邻居,右边的状态先确定,所以从右向左遍历
/**
 * @date: 2024 Jan 06
 * @time: 17:16
 * @author: Chris
 * @title: 135. Candy
 * @link: https://leetcode.cn/problems/candy/
**/
package day34

func candy(ratings []int) int {
	n := len(ratings)
	if n == 0 {
		return 0
	}
	// 初始化,每人一个糖
	candies := make([]int, n)
	for i := range candies {
		candies[i] = 1
	}

	// 确保每个比左邻居评分高的孩子,比左邻居多一颗糖果。
	// 从左到右 --->
	for i := 1; i < n; i++ {
		//评分比左侧高!
		if ratings[i] > ratings[i-1] {
			// 比左侧多一个
			candies[i] = candies[i-1] + 1
		}
	}

	// 确保每个比右邻居评分高的孩子,比右邻居多一颗糖果。
	// 从右到左  <---
	for i := n - 2; i >= 0; i-- {
		//评分比右侧高!  如果糖果少的话再操作,糖果多不要操作,可能会减少糖果!
		// 比如 [1,2,3,1] 糖果数[1,2,3,1],不加这个条件3就变成2了!
		if ratings[i] > ratings[i+1] && candies[i] <= candies[i+1] {
			candies[i] = candies[i+1] + 1
		}
	}
	total := 0
	for _, c := range candies {
		total += c
	}
	return total
}

细节:

左到右→: i =1

右到左←: i = n-2

为什么是评分比较不包含=符号?

题目要求比邻居高,糖果比邻居糖果多,
如果相等(即不比邻居高),那么可以不比邻居多。
比如: ratings[1, 2, 2] 糖果为:[1, 2, 1]

为什么第二次循环多加了一个candies[i] <= candies[i+1]判断,如果不判断有什么后果?

如果当前孩子比右邻居糖果还进行操作,可能会减少糖果!从而导致违背第一次遍历要求!
比如ratings [1,2,3,1] 第一次遍历糖果数[1,2,3,1]
不加这个条件,遍历[1,2,3,1],3就会变成2了!不满足糖果数大于左邻居!

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值