代码随想录算法训练营第二十九天| 134. 加油站、135. 分发糖果、860.柠檬水找零、406.根据身高重建队列

1、134. 加油站

解题核心思路

  1. 如果整个路程中,汽油总量比总消耗量还少,则不可能绕一圈,直接返回 -1

    • 因为油不够,怎么换起点都没用。
  2. 如果汽油总量 >= 总消耗量,那么一定存在一个唯一的起点,可以完成整个旅程。

    • 我们只需要找到这个唯一的起点

详细步骤

Step 1: 计算总油量是否足够

  • 计算 总汽油量 sum(gas) 和 总消耗量 sum(cost)
  • 如果 sum(gas) < sum(cost),说明总油不够,不管从哪个加油站出发都不可能绕完一圈,返回 -1

Step 2: 寻找合适的起点

  • 维护当前油量 tank(从某个加油站出发,油量不能变负)。

    • 遍历每个加油站,计算 当前站点的净收益

                           净收益=gas[i]−cost[i]
  • 如果 tank 变成负数,说明从当前起点 start 不能到达 i+1,我们需要从 i+1 重新开始。

    • 这个时候,我们把 start 设为 i+1,并清空 tank 重新计算。
    • 原因:从 starti 之间的任何站点都不能作为新的起点,否则 tank 还是会变成负数,根本走不下去。
  • 遍历完整个数组后,最终的 start 就是我们要的结果

func canCompleteCircuit(gas []int, cost []int) int {
    currentGas := 0
    totalGas := 0
    start := 0
    for i := 0; i < len(gas); i++ {
        currentGas += gas[i] - cost[i]
        totalGas += gas[i] - cost[i]
        if currentGas < 0 {
            start = i + 1
            currentGas = 0
        }
    }
    if totalGas < 0 {
        return - 1
    }
    return start
}

2、 135. 分发糖果

解题核心思路

1. 规则回顾

  • 每个孩子至少分 1 颗糖果
  • 如果某个孩子的评分比左(右)边的孩子高,则他的糖果数也要比左(右)边的孩子多

2. 采用贪心法,两次遍历

  • 从左到右遍历: 确保右边评分更高的孩子比左边多拿
  • 从右到左遍历: 确保左边评分更高的孩子比右边多拿(同时保证最终的最少糖果数)。

详细步骤

Step 1: 先初始化所有孩子的糖果数为 1

  • 每个孩子至少分 1 颗糖果,所以创建一个数组 candies,所有元素初始化为 1

Step 2: 从左到右遍历,处理「递增关系」

  • 如果 ratings[i] > ratings[i-1],说明 i 号孩子的评分比左边高,所以 i 号孩子的糖果数比 i-1 号孩子多 1: candies[i]=candies[i−1]+1candies[i] = candies[i-1] + 1candies[i]=candies[i−1]+1

Step 3: 从右到左遍历,处理「递减关系」

  • 如果 ratings[i] > ratings[i+1],说明 i 号孩子的评分比右边高,所以 i 号孩子的糖果数也要比 i+1 号孩子多: candies[i]=max⁡(candies[i],candies[i+1]+1)candies[i] = \max(candies[i], candies[i+1] + 1)candies[i]=max(candies[i],candies[i+1]+1)
    • 要取最大值,因为 candies[i] 可能已经在左到右遍历时分配了较大的值

Step 4: 计算总糖果数 

  • 遍历 candies 数组,求和即可。
func candy(ratings []int) int {
    if len(ratings) == 0 {
        return 0
    }
    n := len(ratings)
    candies := make([]int, len(ratings))
    for i := range candies {
        candies[i] = 1
    }
    // 从左到右遍历
    // 右边大的话,加一
    for i := 1; i < len(ratings); i++ {
        if ratings[i] > ratings[i - 1] {
            candies[i] = candies[i - 1] + 1
        }
    }
    // 从右到左遍历
    // 左边大的话,如果已经加过了
    for i := n - 2; i >= 0; i-- {
        if ratings[i] > ratings[i + 1] {
            candies[i] = max(candies[i], candies[i + 1] + 1)
        }
    }

    sum := 0
    for _, c := range candies {
        sum += c
    }
    return sum
}

3、 柠檬水找零

优先找回大的面值

func lemonadeChange(bills []int) bool {
    count5:= 0
    count10 := 0
    count20 := 0
    for i := 0; i < len(bills); i++ {
        if bills[i] == 5{
            count5++
        }
        if bills[i] == 10 {
            count10++
            if count5 > 0{
                count5--
            }else {
                return false
            }
        }
        if bills[i] == 20 {
            count20++
            if count10 > 0 && count5 > 0 {
                count5--
                count10--
            }else if count5 >= 3{
                count5 = count5 - 3
            }else {
                return false
            }
        }
    }
    return true
}

4、406.根据身高重建队列

关键观察

  1. 先考虑身高最高的人

    • 他们的 k 值直接决定了他们在队列中的位置,因为没有比他们更高的人可以影响他们的 k 计数。
  2. 身高相同时,k 小的人先排

    • 因为 k 代表这个人前面必须有 k 个不矮于他的人的数量,身高相同但 k 小的人必须先安排,否则会影响后续插入。

具体步骤

Step 1: 先排序

  • 按身高 h 从高到低排序(因为高的人先确定位置,不受矮的人的影响)。
  • 如果 h 相同,则按 k 从小到大排序(因为 k 小的人必须先插入)。

Step 2: 依次插入

  • 遍历排好序的数组,将元素 k 值插入到队列中的第 k 位置(索引从 0 开始)。
  • 由于之前已经排序,当前插入的人 不会影响已经插入的人的 k,所以插入后队列仍是合法的。
import "sort"

func reconstructQueue(people [][]int) [][]int {
    // Step 1: 排序规则
    sort.Slice(people, func(i, j int) bool {
        if people[i][0] == people[j][0] {
            return people[i][1] < people[j][1]  // k 小的排前面
        }
        return people[i][0] > people[j][0]  // h 高的排前面
    })

    // Step 2: 依次插入到结果数组
    result := [][]int{}
    for _, p := range people {
        index := p[1]
        // 在 index 位置插入 p
        result = append(result[:index], append([][]int{p}, result[index:]...)...)
    }

    return result
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值