【前端刷题】135.分发糖果(HARD)

315 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情

题目(Candy)

链接:https://leetcode-cn.com/problems/candy
解决数:1029
通过率:48.8%
标签:贪心 数组 
相关公司:bytedance amazon google 

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

 

示例 1:

输入: ratings = [1,0,2]
输出: 5
解释: 你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。

示例 2:

输入: ratings = [1,2,2]
输出: 4
解释: 你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。
     第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。

 

提示:

  • n == ratings.length
  • 1 <= n <= 2 * 104
  • 0 <= ratings[i] <= 2 * 104

思路

5种方法实现贪心算法,从布尔值的三态到平衡三进制实现更优解

一 动态规划 · 双指针

解题思路

  • 数组r每人糖果数,初始每人1个,双指针同时分别从 扫描
    • 指针i顺序找当前 > 上个,当前 = 当前上个 + 1 取大
    • 指针j倒序找当前 > 上个,当前 = 当前上个 + 1 取大

image.png

代码

var candy = function(ratings) {
    let i = 0, r = new Uint16Array(ratings.length).fill(1), j = ratings.length - 1
    while (i < ratings.length - 1) {
        if (ratings[++i] > ratings[i - 1]) r[i] = Math.max(r[i], r[i - 1] + 1)
        if (ratings[--j] > ratings[j + 1]) r[j] = Math.max(r[j], r[j + 1] + 1)
    }
    return r.reduce((p, v) => p + v)
};

5.png

二 线性扫描 · 数组

解题思路

  • 数组r每人糖果数,单指针i先顺序再倒序(先倒后顺也可)。以初始第0人1个为例

代码

var candy = function(ratings) {
    let i = 0, r = new Uint16Array(ratings.length)
    r[0] = 1
    while (++i < ratings.length) // 初始每人1个时,上解法保留if,删Math.max即可
        r[i] = ratings[i] > ratings[i - 1] ? r[i - 1] + 1 : 1
    i--
    while (i--) // r[i] = Math.max(r[i], r[i + 1] + 1) 写成判断后,并入条件
        if (ratings[i] > ratings[i + 1] && r[i + 1] + 1 > r[i]) r[i] = r[i + 1] + 1
    return r.reduce((p, v) => p + v)
};

4.png

三 线性扫描 · 变量

解题思路

  • 来自力扣官方
    • 顺序left和倒序right都只关心自己,不用取大,专心赋值
    • 取大在累加r合并leftright时进行,即上解法倒序循环与reduce合并

代码

var candy = function(ratings) {
    let i = 0, left = new Uint16Array(ratings.length), right = 1, r = 0
    left[0] = 1
    while (++i < ratings.length) {
        left[i] = ratings[i] > ratings[i - 1] ? left[i - 1] + 1 : 1
    }
    while (i--) {
        r += left[i] > right ? left[i] : right
        right = ratings[i - 1] > ratings[i] ? right + 1 : 1
    }
    return r
};

1.png

四 区间 · 整型

解题思路

  • 扫描评分,比较前后分数
    • 递增,发n + 1糖果(最小递增区间[1, 2],当前2n最小1)
      • 递减 → 递增:重置n = 1
    • 持平,发1糖果。重置n = 1 m = 0
    • 递减,发m + 1糖果(最小递减区间[2, 1],当前1m最小0)
      • 递增 → 递减:重置m = 0
      • m<nm++ 糖果+1给递减区间最大值
        • m(递减区间最大值)与n(递增区间最大值)相邻
      • m=nm+=21糖给拿n人,避免相邻相等
        • n成为递减区间最大值,新mn - 1相邻,不会再出现相邻相等
      • m>nm++ 糖果+1给递减区间最大值
        • m >= n,最多糖都给拿n的人(波峰)

image.png

代码

var candy = function(ratings) {
    if (ratings.length === 0) return 0
    let i = 0, r = 1, n = 1, m = 0, prev2
    while (++i < ratings.length) {
        const cur = ratings[i], prev = ratings[i - 1]
        if (cur > prev) {
            if (prev < prev2) n = 1
            r += ++n
        } else if (cur === prev) {
            n = 1
            m = 0
            r ++
        } else {
            if (prev > prev2) m = 0
            if (++m === n) m++
            r += m
        }
        prev2 = prev // 上上一人,与prev比较。递减 → 递增 重置 n 递增 → 递减 重置 m
    }
    return r
};

3.png

五 区间 · 布尔值

解题思路

  • 布尔值f三种状态标记:持平undefined 递增true 递减false。代替prev2比较prev

代码

var candy = function(ratings) {
    if (ratings.length === 0) return 0
    let i = 0, r = 1, n = 1, m = 0, f
    while (++i < ratings.length) {
        const cur = ratings[i], prev = ratings[i - 1]
        if (cur > prev) {
            if (f === false) n = 1
            f = true
            r += ++n
        } else if (cur === prev) {
            f = void 0
            n = 1
            m = 0
            r ++
        } else {
            if (f) m = 0
            f = false
            if (++m === n) m++
            r += m
        }
    }
    return r
};

2.png

排行

长度10000安全整型范围随机数组,每种解法求解100次,每秒操作数 image.png

拓展

SQL规范中,布尔值有三个状态,包括真,假和未知,后者在实现时,通常用null表示 部分编程语言,当变量未定义时,或不知道真假时,有默认状态,JSundefined 利用布尔值的三态,我们可以多表示一种状态,但这会产生迷惑代码,例如

let b
if (b === true) {}
else if (b === false) {}
else {}

推荐适合布尔值的场景:一定是二值逻辑,即非 MySQL中,通过指定字段默认值或直接声明tinyint(1),避免null存在 TS中,通过枚举enum定义状态,语义更明确,利于拓展 下面微调布尔值解法,用平衡三进制定义f,即true为1,false为-1,undefined为0

var candy = function(ratings) {
    if (ratings.length === 0) return 0
    let i = 0, r = 1, n = 1, m = 0, f = 0
    while (++i < ratings.length) {
        const cur = ratings[i], prev = ratings[i - 1]
        if (cur > prev) {
            if (f === -1) n = 1
            f = 1
            r += ++n
        } else if (cur === prev) {
            f = 0
            n = 1
            m = 0
            r ++
        } else {
            if (f === 1) m = 0
            f = -1
            if (++m === n) m++
            r += m
        }
    }
    return r
};