贪心算法-种花问题|刷题打卡

93 阅读3分钟

本文正在参与掘金团队号上线活动,点击 查看大厂春招职位

题目描述

假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。

给你一个整数数组  flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false。

来源:力扣(LeetCode)

题目分析

初见思路

阅读完题目,第一个出现在脑海的思路就是,先判断花坛是否可以种花,然后计算出所有可以种花的花坛的数量,再和需要种植的花的数量进行对比,大于需要种植的花的数量就表示可以种入。

程序流程:

  1. 遍历数组
  2. 判断元素相邻的元素是否为0(没有种花),如果可以则count++
    1. 当处理第一个元素与最后一个元素时,需要注意边界问题;为了避免复杂的边界判断,我们将数组首尾添加一个0
    2. 当某一个元素确定已经可以种花时,应该将该元素设置为1(已经种花),才不会影响该元素的下一个元素的逻辑判断;更好的判断是直接跳过下一个元素的处理,因为如果该元素种花,根据规则相邻的花坛不能种花,下一个元素肯定是不能种花的,所以直接跳过。
  3. 当count等于需要种植的花数n,就可以返回true,
  4. 遍历完成之后,程序还没有结束,返回count>=n(为了处理n为0的情况,这里不能直接返回false)。

代码实现:

function canPlaceFlowers(flowerbed: number[], n: number): boolean {
    let count = 0 // 可以种的最大数量
    flowerbed = [0, ...flowerbed, 0]
    for (let i = 1; i < flowerbed.length - 1; i++) {
        if (flowerbed[i] === 1) continue;
        if (flowerbed[i - 1] !== 0) continue;
        if (flowerbed[i + 1] !== 0) continue;
        if (++count >= n) return true
        i++
    }
    return count >= n
};

贪心算法

贪心算法是一种在每一步选择中都采取在当前状态下最好或最优的选择,从而希望得到结果是最好或最优的算法。对于比较大的问题,我们可以分解为更小的问题的叠加,然后求出更小问题的最优解,最后得出较大问题的答案。

思路分析

我对贪心算法的解题思路进行分析,归纳出下面的解题思路:

  1. 使用数学语言描述问题
  2. 将求解的问题分解为若干个子问题
  3. 通过对子问题求解,得出每个问题的最优解
  4. 通过子问题的最优解得出原本问题的解决方案 题目中数组flowerbed中可能存在x个分布不确定的1,情况比较复杂;

我们先把情况简化,假设flowerbed的第一个元素和最后一个元素为1,中间的元素都为0,设第一个元素的索引为minIndex、最后一个元素的索引为maxIndex

那中间元素的数量为maxIndex - minIndex - 1,此时如果中间元素的数量num为奇数,可种植的最大数量count( num - 1 )/ 2num为偶数时,count(num - 2)/ 2,使用向下取整处理,则不管num为奇数还是偶数都有count=(num-1)/ 2 = (maxIndex - minIndex - 2)/2

现在我们把情况复杂化,假设为1的元素不在是flowerbed的第一个和最后一个元素而是,索引为jk的两个元素(j < k)。

此时需要的计算部分被分割为三个部分,array[0, j]array[j, k]array[k-max],其中array[j, k]可以使用我们上面的逻辑进行分析,得到结果Math.floor((k - j - 2) / 2);对于array[0, j]array[k-max]我们也可以转化为上面的情况,当处理array[0, j]时,我们可以视为数组索引为-2的元素为1,索引为-1的元素为0,这样我们处理array[0, j]时,可以认为是计算元素-2和元素j之间最大可种植数,计算方法为Math.floor(j / 2),同理计算出array[k-max]的结果为Math.floor((length - k - 1) / 2)

代码实现

function canPlaceFlowers(flowerbed: number[], n: number): boolean {
    let count = 0 // 可以种的最大数量
    let index = -2 // 存储索引
    for (let i = 0; i < flowerbed.length; i++) {
        if (flowerbed[i] === 1) {
            count += Math.floor((i - index - 2) / 2)
            index = i
            if (count > n) return true
        }
    }
    return (count + Math.floor((flowerbed.length - 1 - index) / 2)) >= n
};

总结

这道题目并不难,但是需要注意边界问题的处理;对于边界问题,有时候我们处理起来比较麻烦,此时我们可以把边界问题转换为一般的情况进行处理,会比较快捷。

对于贪心算法核心其实就是两点:1. 把问题简化,2. 每一步都求最优解。