算法题 种花问题

605. 种花问题

问题描述

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

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

返回 能否 在不违反规则的情况下种入 n 朵花。

示例

输入: flowerbed = [1,0,0,0,1], n = 1
输出: true
解释: 可以在中间的0处种花,得到[1,0,1,0,1]

算法思路

核心思想:贪心算法

  1. 局部最优:尽可能早地种花(从左到右扫描)
  2. 全局最优:最大化可种植数量
  3. 判断条件:只要最大可种植数量 ≥ n,就返回 true

关键:

  • 只有当 flowerbed[i] == 0 且其左右邻居都为 0 时,才能在位置 i 种花
  • 边界情况:首尾位置只有一侧有邻居

优化:

  • 一次遍历:从左到右扫描,遇到可种位置立即种花
  • 原地修改:种花后将 flowerbed[i] 改为 1,影响后续判断

代码实现

class Solution {
    /**
     * 判断是否能在花坛中种入 n 朵花而不违反相邻规则
     * 
     * @param flowerbed 花坛数组,0表示空地,1表示已种花
     * @param n 需要种的花的数量
     * @return 是否能成功种入n朵花
     */
    public boolean canPlaceFlowers(int[] flowerbed, int n) {
        // 特殊情况:不需要种花
        if (n == 0) {
            return true;
        }
        
        int count = 0;  // 记录实际能种的花的数量
        
        for (int i = 0; i < flowerbed.length; i++) {
            // 当前位置为空地,才可能种花
            if (flowerbed[i] == 0) {
                // 检查左右邻居是否都为空
                boolean leftEmpty = (i == 0) || (flowerbed[i - 1] == 0);
                boolean rightEmpty = (i == flowerbed.length - 1) || (flowerbed[i + 1] == 0);
                
                // 如果左右都为空,则可以种花
                if (leftEmpty && rightEmpty) {
                    flowerbed[i] = 1;  // 种花(原地修改)
                    count++;           // 种花数量加1
                    
                    // 优化:如果已达到目标,提前返回
                    if (count >= n) {
                        return true;
                    }
                }
            }
        }
        
        // 检查是否种够了n朵花
        return count >= n;
    }
}

算法分析

时间复杂度:O(m)

  • 其中 m 是 flowerbed 数组的长度
  • 最多遍历一次数组
  • 提前终止优化在某些情况下能减少实际运行时间

空间复杂度:O(1)

  • 只使用常数额外空间
  • 原地修改输入数组(符合题目要求)

算法过程

flowerbed = [1,0,0,0,1], n = 1

iflowerbed当前值leftEmptyrightEmpty可种?操作count
0[1,0,0,0,1]1-true跳过0
1[1,0,0,0,1]0false (1)true (0)跳过0
2[1,0,0,0,1]0true (0)true (0)种花→11
------提前返回 true-

结果:true

flowerbed = [1,0,0,0,1], n = 2

iflowerbed当前值leftEmptyrightEmpty可种?操作count
0[1,0,0,0,1]1-true跳过0
1[1,0,0,0,1]0falsetrue跳过0
2[1,0,0,0,1]0truetrue种花→11
3[1,0,1,0,1]0false (1)false (1)跳过1
4[1,0,1,0,1]1--跳过1

结果:false(只能种1朵,需要2朵)

测试用例

public static void main(String[] args) {
    Solution solution = new Solution();
    
    // 测试用例1:标准示例
    int[] bed1 = {1,0,0,0,1};
    System.out.println("Test 1: " + solution.canPlaceFlowers(bed1, 1)); // true
    
    // 测试用例2:需要种2朵
    int[] bed2 = {1,0,0,0,1};
    System.out.println("Test 2: " + solution.canPlaceFlowers(bed2, 2)); // false
    
    // 测试用例3:单地块
    int[] bed3 = {0};
    System.out.println("Test 3: " + solution.canPlaceFlowers(bed3, 1)); // true
    
    // 测试用例4:全空地
    int[] bed4 = {0,0,0,0,0};
    System.out.println("Test 4: " + solution.canPlaceFlowers(bed4, 3)); // true
    // 可种在0,2,4位置
    
    // 测试用例5:全已种
    int[] bed5 = {1,1,1,1};
    System.out.println("Test 5: " + solution.canPlaceFlowers(bed5, 1)); // false
    
    // 测试用例6:不需要种花
    int[] bed6 = {1,0,1};
    System.out.println("Test 6: " + solution.canPlaceFlowers(bed6, 0)); // true
    
    // 测试用例7:边界情况
    int[] bed7 = {0,0,1,0,0};
    System.out.println("Test 7: " + solution.canPlaceFlowers(bed7, 1)); // true
    // 可在位置0或4种花
    
    // 测试用例8:交替模式
    int[] bed8 = {0,1,0,1,0};
    System.out.println("Test 8: " + solution.canPlaceFlowers(bed8, 1)); // false
    // 没有连续两个0
    
    // 测试用例9:长序列
    int[] bed9 = {0,0,1,0,0,0,1,0,0};
    System.out.println("Test 9: " + solution.canPlaceFlowers(bed9, 2)); // true
    // 可在位置0,4,8种花,最多种3朵
}

关键点

  1. 边界处理

    • 首位置(i=0):没有左邻居,视为左空
    • 尾位置(i=length-1):没有右邻居,视为右空
    • 使用 (i == 0)(i == length-1) 判断
  2. 贪心正确性

    • 尽早种花不会影响后续最优解
    • 如果跳过一个可种位置,后续可种位置不会增加
    • 局部最优导致全局最优
  3. 原地修改

    • 种花后将 flowerbed[i] = 1
    • 影响后续位置的判断(正确反映种植规则)
  4. 提前终止

    • 一旦种够 n 朵花,立即返回 true
    • 避免不必要的遍历

常见问题

  1. 为什么不计算理论最大值再比较?
    虽然可以数学计算最大可种数量,但贪心模拟更直观且代码简洁。两种方法时间复杂度相同。

  2. 能否不修改原数组?
    可以,但需要额外空间记录已种花的位置。原地修改更节省空间。

  3. 如何处理极端情况?

    • 空数组:flowerbed.length == 0,返回 n == 0
    • n 值:提前终止优化能快速处理
  4. 算法的鲁棒性
    代码已处理所有边界情况,包括:

    • 单元素数组
    • 全0或全1数组
    • n=0的特殊情况
  5. 是否有数学公式解?
    可以,通过计算连续0的段长来计算最大可种数量,但实现更复杂:

    // 对每段连续k个0,可种花数量为 (k+1)/2
    // 需要特殊处理首尾段
    

    贪心模拟更直观易懂。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值