练习 1:活动区间调度(贪心入门)
给定多个活动区间 [start,end],同一时间只能参加一个活动,求最多能参加多少个活动。
贪心核心思想
局部最优→ 全局最优:每次选结束时间最早的活动,结束越早,剩余时间越多,能选更多活动
代码及解析
package main
import (
"fmt"
"sort"
)
// 定义活动结构体
type Interval struct {
start int
end int
}
func maxActivity(intervals []Interval) int {
// 1.贪心第一步:排序
// 按结束时间升序排列
sort.Slice(intervals, func(i, j int) bool {
return intervals[i].end < intervals[j].end
})
count := 0
lastEnd := -1 // 记录上一个活动的结束时间
// 2.遍历选择
for _, cur := range intervals {
// 当前活动开始 >= 上一个结束 → 不重叠,可以选
if cur.start >= lastEnd {
count++
lastEnd = cur.end
}
}
return count
}
func main() {
list := []Interval{
{1, 3},
{2, 4},
{3, 5},
{6, 7},
}
fmt.Println(maxActivity(list)) // 输出:3
}
步骤拆解:
- 排序:让所有活动按结束时间从小到大;
- 初始化:记录上一场活动结束时间;
- 遍历:
- 不重叠 → 选中,更新结束时间;
- 重叠 → 直接跳过;
- 最终统计选中总数。
练习 2:分发饼干(LeetCode 455 极简贪心)

贪心核心思想
局部最优:最小的饼干满足胃口最小的孩子,节省大饼干留给大胃口。
代码及解析
package main
import (
"fmt"
"sort"
)
func findContentChildren(g []int, s []int) int {
// 1.全部排序
sort.Ints(g)
sort.Ints(s)
child := 0
cookie := 0
// 2.双指针遍历
for child < len(g) && cookie < len(s) {
// 当前饼干能满足孩子
if s[cookie] >= g[child] {
child++ // 满足人数+1
cookie++ // 换下一块饼干
} else {
// 饼干太小,直接换下一块
cookie++
}
}
return child
}
func main() {
g := []int{1, 2, 3}
s := []int{1, 1}
fmt.Println(findContentChildren(g, s)) // 输出:1
}
步骤拆解
- 孩子、饼干数组全部升序排序;
- 双指针:分别指向当前孩子、当前饼干;
- 饼干够吃:满足孩子,两个指针都后移;
- 饼干不够:只换更大的饼干;
- 最终满足的孩子数即为答案。
练习 3:LeetCode 68 文本左右对齐(进阶贪心)

贪心核心思想
局部最优:每一行尽可能多装入单词,保证整体排版最优、行数最少。
代码及解析
package main
import (
"fmt"
"strings"
)
func fullJustify(words []string, maxWidth int) []string {
var result []string
totalWords := len(words)
index := 0 // 当前处理到第几个单词
// ===================== 步骤 1:循环处理每一行 =====================
for index < totalWords {
// ===================== 步骤 2:贪心选择:当前行最多放哪些单词 =====================
startIndex := index // 本行第一个单词
currentLineLength := len(words[index])
index++
// 能放下就继续放:空格 + 单词长度不超过 maxWidth
for index < totalWords && currentLineLength+1+len(words[index]) <= maxWidth {
currentLineLength += 1 + len(words[index])
index++
}
// ===================== 步骤 3:计算空格数量 =====================
wordCount := index - startIndex // 本行单词数
allWordsLength := currentLineLength - (wordCount - 1) // 纯单词总长度
totalSpaces := maxWidth - allWordsLength // 本行需要填充的总空格数
var currentLine string
// ===================== 步骤 4:分情况构建行 =====================
// 情况 A:最后一行 或 只有一个单词 → 左对齐
if index == totalWords || wordCount == 1 {
currentLine = words[startIndex]
// 单词之间只加一个空格
for i := startIndex + 1; i < index; i++ {
currentLine += " " + words[i]
}
// 末尾补空格
for len(currentLine) < maxWidth {
currentLine += " "
}
} else {
// 情况 B:普通行 → 左右对齐,空格均匀分配
gapCount := wordCount - 1 // 单词间的空隙数
baseSpaces := totalSpaces / gapCount // 每个空隙最少空格数
extraSpaces := totalSpaces % gapCount // 前几个空隙要多一个空格
currentLine = ""
for i := startIndex; i < index; i++ {
currentLine += words[i]
// 不是最后一个单词就补空格
if i != index-1 {
if i-startIndex < extraSpaces {
currentLine += strings.Repeat(" ", baseSpaces+1)
} else {
currentLine += strings.Repeat(" ", baseSpaces)
}
}
}
}
// 把当前行加入结果集
result = append(result, currentLine)
}
return result
}
func main() {
words := []string{"This", "is", "an", "example", "of", "text", "justification."}
res := fullJustify(words, 16)
for _, line := range res {
fmt.Println("|" + line + "|")
}
}
核心分段拆解
- 贪心选行从当前单词开始,不断往后加,直到再加一个单词 + 空格就超宽度;
- 长度计算算出所有单词纯长度,用总行宽减去单词长度,得到需要填充的全部空格;
- 分支处理
- 最后一行:单词间单个空格,后面补空格;
- 普通行:均分空格,多余空格优先补在左侧间隔
三篇练习 统一总结
1.贪心算法核心:局部最优 → 全局最优;
2.通用解题步骤:
确定贪心策略;
排序 / 顺序遍历;
逐一步骤做选择,不回溯、不反悔;
3.Go 贪心高频场景:字符串排版:
贪心填充 + 规则化拼接。数组匹配:
双指针 + 排序;
区间问题:
按结束 / 开始排序;
3541

被折叠的 条评论
为什么被折叠?



