如何在Java中实现高效的贪心算法:从最小生成树到背包问题

如何在Java中实现高效的贪心算法:从最小生成树到背包问题

大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!

贪心算法是一种在解决优化问题时非常有效的算法设计策略。它的核心思想是在每一步选择中都采取当前看起来最优的选择,从而希望通过一系列局部最优的选择来得到全局最优解。贪心算法广泛应用于许多实际问题中,包括最小生成树、背包问题等。本文将深入探讨如何在Java中实现高效的贪心算法,并以实际代码示例说明其应用。

一、最小生成树

最小生成树(MST)问题是图论中的一个经典问题,目的是在一个加权图中找到一个树,连接所有顶点,并使得树的边权之和最小。常用的贪心算法有Prim算法和Kruskal算法。

1. Kruskal算法

Kruskal算法通过不断选择当前图中权值最小的边来构建最小生成树。

package cn.juwatech.greedy;

import java.util.*;

public class KruskalAlgorithm {
    
    static class Edge {
        int src, dest, weight;

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

    static class DisjointSet {
        int[] parent, rank;

        DisjointSet(int n) {
            parent = new int[n];
            rank = new int[n];
            for (int i = 0; i < n; i++) {
                parent[i] = i;
                rank[i] = 0;
            }
        }

        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) {
                if (rank[rootU] > rank[rootV]) {
                    parent[rootV] = rootU;
                } else if (rank[rootU] < rank[rootV]) {
                    parent[rootU] = rootV;
                } else {
                    parent[rootV] = rootU;
                    rank[rootU]++;
                }
            }
        }
    }

    public static void kruskal(List<Edge> edges, int numVertices) {
        Collections.sort(edges, Comparator.comparingInt(e -> e.weight));
        DisjointSet ds = new DisjointSet(numVertices);

        List<Edge> mst = new ArrayList<>();
        for (Edge edge : edges) {
            int rootU = ds.find(edge.src);
            int rootV = ds.find(edge.dest);
            if (rootU != rootV) {
                mst.add(edge);
                ds.union(rootU, rootV);
            }
        }

        System.out.println("Minimum Spanning Tree:");
        for (Edge e : mst) {
            System.out.println(e.src + " -- " + e.dest + " == " + e.weight);
        }
    }

    public static void main(String[] args) {
        List<Edge> edges = 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 numVertices = 4;

        kruskal(edges, numVertices);
    }
}
2. Prim算法

Prim算法从一个顶点开始,逐步扩展最小生成树。

package cn.juwatech.greedy;

import java.util.*;

public class PrimAlgorithm {

    static class Edge {
        int src, dest, weight;

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

    public static void prim(List<Edge> edges, int numVertices) {
        PriorityQueue<Edge> pq = new PriorityQueue<>(Comparator.comparingInt(e -> e.weight));
        boolean[] inMST = new boolean[numVertices];
        int[] parent = new int[numVertices];
        int[] key = new int[numVertices];

        Arrays.fill(key, Integer.MAX_VALUE);
        key[0] = 0;
        pq.add(new Edge(-1, 0, 0));

        while (!pq.isEmpty()) {
            Edge edge = pq.poll();
            int u = edge.dest;
            if (inMST[u]) continue;

            inMST[u] = true;
            if (edge.src != -1) {
                System.out.println(edge.src + " -- " + u + " == " + edge.weight);
            }

            for (Edge e : edges) {
                if (e.src == u && !inMST[e.dest] && e.weight < key[e.dest]) {
                    key[e.dest] = e.weight;
                    pq.add(new Edge(u, e.dest, e.weight));
                    parent[e.dest] = u;
                }
            }
        }
    }

    public static void main(String[] args) {
        List<Edge> edges = 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 numVertices = 4;

        prim(edges, numVertices);
    }
}

二、背包问题

背包问题是一类经典的优化问题,其中最常见的是0/1背包问题。贪心算法在0/1背包问题中不是最优解,但在分数背包问题中可以有效解决。

1. 分数背包问题

分数背包问题允许将物品分割以便尽可能多地装入背包。

package cn.juwatech.greedy;

import java.util.Arrays;

public class FractionalKnapsack {

    static class Item {
        int value, weight;

        Item(int value, int weight) {
            this.value = value;
            this.weight = weight;
        }

        double getValuePerWeight() {
            return (double) value / weight;
        }
    }

    public static double fractionalKnapsack(Item[] items, int capacity) {
        Arrays.sort(items, (a, b) -> Double.compare(b.getValuePerWeight(), a.getValuePerWeight()));

        double totalValue = 0.0;
        for (Item item : items) {
            if (capacity == 0) break;

            int takenWeight = Math.min(item.weight, capacity);
            totalValue += takenWeight * item.getValuePerWeight();
            capacity -= takenWeight;
        }

        return totalValue;
    }

    public static void main(String[] args) {
        Item[] items = {
            new Item(60, 10),
            new Item(100, 20),
            new Item(120, 30)
        };
        int capacity = 50;

        double maxValue = fractionalKnapsack(items, capacity);
        System.out.println("Maximum value in Knapsack = " + maxValue);
    }
}

三、总结

本文介绍了如何在Java中实现高效的贪心算法,包括最小生成树的Kruskal和Prim算法,以及分数背包问题的解决方法。贪心算法是一种简单而有效的算法设计策略,通过合理选择局部最优解,可以快速找到问题的近似解。掌握这些贪心算法的实现,不仅能帮助解决实际问题,还能提升算法设计和优化能力。

本文著作权归聚娃科技微赚淘客系统开发者团队,转载请注明出处!

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值