目录
贪心算法(Greedy Algorithm)是一种通过每一步选择当前最优解来构建全局最优解的算法策略。它在优化问题中尤其有效,尽管不一定适用于所有问题。本文将介绍贪心算法的基本概念,并提供 Python、Go 和 Java 的详细实现案例。
贪心算法的基本思想
- 选择性质:在每一步做出局部最优选择。
- 可行性:确保选择的解是有效的。
- 最优性:保证局部最优解能引导到全局最优解。
- 解决子问题:逐步解决更小的子问题。
经典案例:活动选择问题
问题描述
给定一组活动,每个活动都有开始和结束时间,目标是选择尽可能多的互不重叠的活动。
Python 实现
def activity_selection(activities):
# 按照结束时间排序
activities.sort(key=lambda x: x[1])
selected_activities = [activities[0]] # 选择第一个活动
last_end_time = activities[0][1] # 更新最后结束时间
# 遍历剩下的活动
for i in range(1, len(activities)):
# 如果当前活动的开始时间 >= 上一个活动的结束时间
if activities[i][0] >= last_end_time:
selected_activities.append(activities[i]) # 选择该活动
last_end_time = activities[i][1] # 更新结束时间
return selected_activities
# 测试
activities = [(1, 3), (2, 5), (4, 6), (6, 7), (5, 8), (8, 9)]
result = activity_selection(activities)
print("Selected activities:", result) # 输出选定的活动
Go 实现
package main
import (
"fmt"
"sort"
)
type Activity struct {
start int
end int
}
// 按照结束时间排序
type ByEnd []Activity
func (a ByEnd) Len() int { return len(a) }
func (a ByEnd) Less(i, j int) bool { return a[i].end < a[j].end }
func (a ByEnd) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func activitySelection(activities []Activity) []Activity {
sort.Sort(ByEnd(activities)) // 对活动进行排序
selectedActivities := []Activity{activities[0]} // 选择第一个活动
lastEndTime := activities[0].end // 更新最后结束时间
// 遍历剩下的活动
for i := 1; i < len(activities); i++ {
// 如果当前活动的开始时间 >= 上一个活动的结束时间
if activities[i].start >= lastEndTime {
selectedActivities = append(selectedActivities, activities[i]) // 选择该活动
lastEndTime = activities[i].end // 更新结束时间
}
}
return selectedActivities
}
func main() {
activities := []Activity{
{1, 3},
{2, 5},
{4, 6},
{6, 7},
{5, 8},
{8, 9},
}
result := activitySelection(activities)
fmt.Println("Selected activities:")
for _, activity := range result {
fmt.Printf("(%d, %d) ", activity.start, activity.end) // 输出选定的活动
}
}
Java 实现
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
class Activity {
int start;
int end;
Activity(int start, int end) {
this.start = start;
this.end = end;
}
}
public class ActivitySelection {
public static ArrayList<Activity> activitySelection(Activity[] activities) {
// 按照结束时间排序
Arrays.sort(activities, Comparator.comparingInt(a -> a.end));
ArrayList<Activity> selectedActivities = new ArrayList<>();
selectedActivities.add(activities[0]); // 选择第一个活动
int lastEndTime = activities[0].end; // 更新最后结束时间
// 遍历剩下的活动
for (int i = 1; i < activities.length; i++) {
// 如果当前活动的开始时间 >= 上一个活动的结束时间
if (activities[i].start >= lastEndTime) {
selectedActivities.add(activities[i]); // 选择该活动
lastEndTime = activities[i].end; // 更新结束时间
}
}
return selectedActivities;
}
public static void main(String[] args) {
Activity[] activities = {
new Activity(1, 3),
new Activity(2, 5),
new Activity(4, 6),
new Activity(6, 7),
new Activity(5, 8),
new Activity(8, 9),
};
ArrayList<Activity> result = activitySelection(activities);
System.out.println("Selected activities:");
for (Activity activity : result) {
System.out.printf("(%d, %d) ", activity.start, activity.end); // 输出选定的活动
}
}
}
另一个经典案例:最小生成树(Kruskal 算法)
问题描述
Kruskal 算法用于寻找加权无向图的最小生成树。
假设你需要为一个城市规划建设一套网络,比如水管或电缆,连接所有的建筑物。目标是最小化建设成本,而每条管道的成本由其长度或其他因素决定。通过构建最小生成树,你可以找到连接所有建筑物的最低成本网络配置。
Python 实现
class DisjointSet:
def __init__(self, n):
self.parent = list(range(n))
def find(self, u):
if self.parent[u] != u:
self.parent[u] = self.find(self.parent[u])
return self.parent[u]
def union(self, u, v):
root_u = self.find(u)
root_v = self.find(v)
if root_u != root_v:
self.parent[root_u] = root_v
def kruskal(vertices, edges):
edges.sort(key=lambda x: x[2]) # 按权重排序
ds = DisjointSet(vertices)
mst = []
total_cost = 0
for u, v, weight in edges:
if ds.find(u) != ds.find(v): # 检查是否在同一集合
ds.union(u, v) # 合并集合
mst.append((u, v, weight)) # 添加边到最小生成树
total_cost += weight # 更新总成本
return mst, total_cost
# 测试
vertices = 4
edges = [(0, 1, 10), (0, 2, 6), (0, 3, 5), (1, 3, 15), (2, 3, 4)]
mst, cost = kruskal(vertices, edges)
print("Edges in the Minimum Spanning Tree:", mst)
print("Total cost:", cost)
Go 实现
package main
import (
"fmt"
"sort"
)
type Edge struct {
u, v, weight int
}
// 按权重排序
type ByWeight []Edge
func (a ByWeight) Len() int { return len(a) }
func (a ByWeight) Less(i, j int) bool { return a[i].weight < a[j].weight }
func (a ByWeight) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
type DisjointSet struct {
parent []int
}
func NewDisjointSet(n int) *DisjointSet {
ds := &DisjointSet{parent: make([]int, n)}
for i := 0; i < n; i++ {
ds.parent[i] = i // 初始化集合
}
return ds
}
func (ds *DisjointSet) Find(u int) int {
if ds.parent[u] != u {
ds.parent[u] = ds.Find(ds.parent[u]) // 路径压缩
}
return ds.parent[u]
}
func (ds *DisjointSet) Union(u, v int) {
rootU := ds.Find(u)
rootV := ds.Find(v)
if rootU != rootV {
ds.parent[rootU] = rootV // 合并集合
}
}
func kruskal(vertices int, edges []Edge) ([]Edge, int) {
sort.Sort(ByWeight(edges)) // 对边进行排序
ds := NewDisjointSet(vertices)
mst := []Edge{}
totalCost := 0
for _, edge := range edges {
if ds.Find(edge.u) != ds.Find(edge.v) {
ds.Union(edge.u, edge.v) // 合并集合
mst = append(mst, edge) // 添加边到最小生成树
totalCost += edge.weight // 更新总成本
}
}
return mst, totalCost
}
func main() {
vertices := 4
edges := []Edge{
{0, 1, 10},
{0, 2, 6},
{0, 3, 5},
{1, 3, 15},
{2, 3, 4},
}
mst, cost := kruskal(vertices, edges)
fmt.Println("Edges in the Minimum Spanning Tree:")
for _, edge := range mst {
fmt.Printf("(%d, %d) with weight %d\n", edge.u, edge.v, edge.weight)
}
fmt.Println("Total cost:", cost)
}
Java 实现
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
class Edge {
int u, v, weight;
Edge(int u, int v, int weight) {
this.u = u;
this.v = v;
this.weight = weight;
}
}
class DisjointSet {
int[] parent;
DisjointSet(int n) {
parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i; // 初始化集合
}
}
int find(int u) {
if (parent[u] != u) {
parent[u] = find(parent[u]); // 路径压缩
}
return parent[u];
}
void union(int u, int v) {
int rootU = find(u);
int rootV = find(v);
if (rootU != rootV) {
parent[rootU] = rootV; // 合并集合
}
}
}
public class Kruskal {
public static ArrayList<Edge> kruskal(int vertices, Edge[] edges) {
Arrays.sort(edges, Comparator.comparingInt(e -> e.weight)); // 按权重排序
DisjointSet ds = new DisjointSet(vertices);
ArrayList<Edge> mst = new ArrayList<>();
int totalCost = 0;
for (Edge edge : edges) {
if (ds.find(edge.u) != ds.find(edge.v)) {
ds.union(edge.u, edge.v); // 合并集合
mst.add(edge); // 添加边到最小生成树
totalCost += edge.weight; // 更新总成本
}
}
System.out.println("Total cost: " + totalCost);
return mst;
}
public static void main(String[] args) {
int vertices = 4;
Edge[] edges = {
new Edge(0, 1, 10),
new Edge(0, 2, 6),
new Edge(0, 3, 5),
new Edge(1, 3, 15),
new Edge(2, 3, 4),
};
ArrayList<Edge> result = kruskal(vertices, edges);
System.out.println("Edges in the Minimum Spanning Tree:");
for (Edge edge : result) {
System.out.printf("(%d, %d) with weight %d\n", edge.u, edge.v, edge.weight);
}
}
}