【算法】贪心算法详解及实现(python、go、java)

目录

贪心算法的基本思想

经典案例:活动选择问题

问题描述

Python 实现

Go 实现

Java 实现

另一个经典案例:最小生成树(Kruskal 算法)

问题描述

Python 实现

Go 实现

Java 实现


贪心算法(Greedy Algorithm)是一种通过每一步选择当前最优解来构建全局最优解的算法策略。它在优化问题中尤其有效,尽管不一定适用于所有问题。本文将介绍贪心算法的基本概念,并提供 Python、Go 和 Java 的详细实现案例。

贪心算法的基本思想

  1. 选择性质:在每一步做出局部最优选择。
  2. 可行性:确保选择的解是有效的。
  3. 最优性:保证局部最优解能引导到全局最优解。
  4. 解决子问题:逐步解决更小的子问题。

经典案例:活动选择问题

问题描述

给定一组活动,每个活动都有开始和结束时间,目标是选择尽可能多的互不重叠的活动。

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);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值