贪心算法详解:理解贪心算法看这一篇就够了

1. 贪心算法的基础理论

1.1 什么是贪心选择性质

贪心选择性质是指一个全局最优解可以通过一系列局部最优的选择构建出来。这意味着在做出每个选择时,只需要考虑当前状态下的最优选择,而不需要考虑未来的后果。例如,在活动选择问题中,总是优先选择最早结束的活动,这给后续活动留出了尽可能多的时间,从而有可能容纳更多的活动。

1.2 证明贪心选择性质

对于每一个使用贪心策略的问题,必须证明贪心选择性质成立。即证明每次选择当前看来最优的选项不会排除任何可能的最优解。这通常需要数学归纳法或反证法等证明方法。

2. 设计步骤

2.1 定义问题和目标

明确你要解决的问题是什么,以及你想要达到的目标(如最大化收益、最小化成本等)。

2.2 确定数据结构

根据问题需求,选择合适的数据结构来表示输入数据。常见的选择包括数组、列表、堆栈、队列、哈希表等。

2.3 排序和选择策略

如果适用,对输入数据进行排序,以便按照某个标准(如最小值、最大值)进行选择。自定义比较器可以帮助我们根据特定条件排序。

2.4 迭代与决策

遍历经过排序的数据集,根据贪心选择性质做出决策。在每次迭代中,检查当前元素是否满足加入解决方案的标准。如果满足,则将其加入到解决方案中,并更新状态变量(如上一次选择的时间点)以影响后续选择。

2.5 终止条件

当所有可能的选择都已经被考虑过,或者达到了某种终止条件(如选择了足够的元素),则停止迭代并返回结果。

3. 实例详解

我们将通过几个具体的例子来详细说明如何在Java中实现贪心算法。

3.1 活动选择问题
import java.util.Arrays;
import java.util.Comparator;

public class ActivitySelection {

    // 定义一个类来表示活动
    static class Activity {
        int start, finish;

        public Activity(int start, int finish) {
            this.start = start;
            this.finish = finish;
        }

        @Override
        public String toString() {
            return "(" + start + ", " + finish + ")";
        }
    }

    // 活动选择方法
    public static void selectActivities(Activity[] activities) {
        // 首先按照活动的结束时间升序排序
        Arrays.sort(activities, Comparator.comparingInt(a -> a.finish));

        System.out.print("Selected Activities: " + activities[0]);
        int lastFinishTime = activities[0].finish;

        for (int i = 1; i < activities.length; i++) {
            if (activities[i].start >= lastFinishTime) {
                lastFinishTime = activities[i].finish;
                System.out.print(", " + activities[i]);
            }
        }
        System.out.println();
    }

    // 测试函数
    public static void main(String[] args) {
        Activity[] activities = {
            new Activity(1, 2),
            new Activity(3, 4),
            new Activity(0, 6),
            new Activity(5, 7),
            new Activity(8, 9),
            new Activity(5, 9)
        };

        selectActivities(activities);
    }
}
3.2 分数背包问题
import java.util.*;

class FractionalKnapsack {
    // 定义一个类来表示每个物品
    static class ItemValue implements Comparable<ItemValue> {
        int weight, value;
        double ratio;

        public ItemValue(int value, int weight) {
            this.value = value;
            this.weight = weight;
            this.ratio = (double)value / weight;
        }

        @Override
        public int compareTo(ItemValue other) {
            return Double.compare(other.ratio, this.ratio);
        }
    }

    // 贪心算法实现
    public static double getMaxValue(int[] weights, int[] values, int capacity) {
        List<ItemValue> itemValues = new ArrayList<>();
        for (int i = 0; i < weights.length; i++) {
            itemValues.add(new ItemValue(values[i], weights[i]));
        }

        // 按照单位价值降序排序
        Collections.sort(itemValues);

        double totalValue = 0.0;
        for (ItemValue item : itemValues) {
            if (capacity >= item.weight) {
                capacity -= item.weight;
                totalValue += item.value;
            } else {
                totalValue += item.ratio * capacity;
                break;
            }
        }
        return totalValue;
    }

    // 测试函数
    public static void main(String[] args) {
        int[] weights = {10, 20, 30};
        int[] values = {60, 100, 120};
        int capacity = 50;
        System.out.println("Maximum value in Knapsack: " + getMaxValue(weights, values, capacity));
    }
}
3.3 最小生成树(Kruskal算法)
import java.util.*;

class KruskalMST {
    // 边类
    static class Edge implements Comparable<Edge> {
        int src, dest, weight;

        public Edge(int src, int dest, int weight) {
            this.src = src;
            this.dest = dest;
            this.weight = weight;
        }

        @Override
        public int compareTo(Edge compareEdge) {
            return this.weight - compareEdge.weight;
        }
    }

    // 并查集(Union-Find)
    static class Subset {
        int parent, rank;
    }

    // 查找集合的根节点
    int find(Subset subsets[], int i) {
        if (subsets[i].parent != i)
            subsets[i].parent = find(subsets, subsets[i].parent);
        return subsets[i].parent;
    }

    // 合并两个子集
    void Union(Subset subsets[], int x, int y) {
        int xroot = find(subsets, x);
        int yroot = find(subsets, y);

        if (subsets[xroot].rank < subsets[yroot].rank)
            subsets[xroot].parent = yroot;
        else if (subsets[xroot].rank > subsets[yroot].rank)
            subsets[yroot].parent = xroot;
        else {
            subsets[yroot].parent = xroot;
            subsets[xroot].rank++;
        }
    }

    // Kruskal算法实现
    void KruskalMST(ArrayList<Edge> edges, int V) {
        // 结果存储最小生成树的边
        ArrayList<Edge> result = new ArrayList<>();

        // 对边按照权重升序排序
        Collections.sort(edges);

        // 创建V个子集
        Subset subsets[] = new Subset[V];
        for (int v = 0; v < V; ++v) {
            subsets[v] = new Subset();
            subsets[v].parent = v;
            subsets[v].rank = 0;
        }

        int e = 0; // 已添加到结果中的边的数量
        int i = 0; // 当前处理的边的索引
        while (e < V - 1 && i < edges.size()) {
            Edge next_edge = edges.get(i++);

            int x = find(subsets, next_edge.src);
            int y = find(subsets, next_edge.dest);

            // 如果包含这条边不会形成环,则添加它到结果中
            if (x != y) {
                result.add(next_edge);
                e++;
                Union(subsets, x, y);
            }
        }

        // 打印最小生成树
        System.out.println("Following are the edges in the constructed MST");
        for (Edge edge : result) {
            System.out.println(edge.src + " -- " + edge.dest + " == " + edge.weight);
        }
    }

    // 测试函数
    public static void main(String[] args) {
        ArrayList<Edge> edges = new ArrayList<>(Arrays.asList(
            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)
        ));

        int V = 4; // 图中顶点的数量
        KruskalMST k = new KruskalMST();
        k.KruskalMST(edges, V);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值