题目描述
花坛里 1 表示已有花、0 表示空。花不能相邻。问还能不能再种下 n 朵。
思路解析
想「怎么摆才能种最多」容易绕进去:要不要留空、先种左还是先种右……其实顺着扫,遇到能种的位置直接种,是最优的,不用回头反悔。
为什么贪心成立:从左往右走,一旦某个 0 的左右都空就立刻种下。早种不会挤占后面更该种的位置,所以「能种就种」一定能凑出最多的花。
准备 · 两端补虚拟 0:从下标 0 扫到末尾,count 记已种几朵。约定:花坛最左边的左边、最右边的右边都视为空地 0,这样首尾格子也能正常判断。
i=0 · 值是 1:下标 0 本身是 1,已经有花了,没法种,直接跳过。
i=1 · 右邻居有花:下标 1 是 0,但它左边是 1(已有花)。种下去就挨着了,违反规则。这是负例:相邻已有花,不能种。
i=2 · 检查左右:下标 2 是 0,先看左右:左边(下标 1)是 0、右边(下标 3)也是 0,左右都空,满足种花条件。
i=2 · 种下 · 关键:关键一步:立刻在下标 2 种花,把这一格就地置成 1,count 加到 1。置 1 很重要——下一步检查下标 3 时就能看到它已被占。
i=3 · 左邻居刚种了:下标 3 是 0,但左边下标 2 刚被我们种成了 1。所以这里不能再种——种花后顺手把格子改成 1,后面就能正确避开。
i=4 · 值是 1:下标 4 本身是 1,跳过。扫到头了。
结束 · 判断够不够:一趟扫完,总共种下 1 朵(在下标 2)。需要 n=1,count=1 已经够了,返回 true。
贪心的关键是「无后效性」:每个位置只要满足左右都空就种,早种不会害到后面。所以一趟从左到右扫下来就是全局最优,不用回溯。
参考代码(Python)
1def canPlaceFlowers(flowerbed, n):
2 count = 0
3 m = len(flowerbed)
4 for i in range(m):
5 if (flowerbed[i] == 0
6 and (i == 0 or flowerbed[i-1] == 0) # 左边空或越界
7 and (i == m-1 or flowerbed[i+1] == 0)): # 右边空或越界
8 flowerbed[i] = 1 # 种下,改成 1 防后面误判
9 count += 1
10 return count >= n # 够 n 朵就行复杂度分析
- 时间复杂度:O(n) —— 花坛只扫一遍
- 空间复杂度:O(1) —— 原地改 flowerbed,只用一个 count 计数
套路模板
记住骨架:两端越界当虚拟 0、能种就种、就地置 1。这套「贪心 + 虚拟边界」在很多扫描类题里都好用。
1count = 0
2for i in range(m):
3 左空 = (i == 0 or a[i-1] == 0) # 越界当作空
4 右空 = (i == m-1 or a[i+1] == 0)
5 if a[i] == 0 and 左空 and 右空:
6 a[i] = 1; count += 1 # 种下并就地标记
7return count >= n易错点
- 错误写法:不处理首尾,i-1 / i+1 直接越界 → 正确写法:用 i==0、i==m-1 把两端当虚拟 0(flowerbed=[0] 时首尾就是同一格,不补虚拟边界会数组越界或漏种这朵)
- 错误写法:判定能种后忘了把 flowerbed[i] 置 1 → 正确写法:种下后立刻 flowerbed[i]=1(不就地标记,[0,0,0] 会把相邻两格都判成可种,连着种违反不相邻规则)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。