贪心算法在Python、JavaScript、Java、C++和C#中的多样化实现及其在钱币找零、分数骑士、活动选择、最小生成树与哈夫曼编码问题中的应用示例

发布于:2024-04-30 ⋅ 阅读:(95) ⋅ 点赞:(0)

贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法策略。贪心算法在有最优子结构的问题中尤为有效。下面我将提供5种不同编程语言实现的贪心算法示例,包括活动选择问题、钱币找零问题等。

1. Python - 钱币找零问题

def coinChange(coins, amount):
    # 贪心策略:总是优先使用最大面额的钱币
    coin_count = [0] * (amount + 1)
    coin_count[0] = 1
    
    for coin in coins:
        for higher_value in range(coin, amount + 1):
            coin_count[higher_value] += coin_count[higher_value - coin]
    
    return coin_count[amount]

# 钱币面额和目标金额
coins = [1, 2, 5]
amount = 5

print(coinChange(coins, amount))  # 输出可能的钱币组合数

详解:在钱币找零问题中,贪心算法总是优先考虑最大面额的钱币,从而减少所需钱币的数量。

2. JavaScript - 分数骑士问题

function fractionKnapsack(capacity, weights, values, n) {
    let i = 0, j = 0;
    while (capacity > 0 && i < n) {
        if (weights[j] <= capacity) {
            capacity -= weights[j];
            j++;
        } else {
            let fraction = Math.floor(capacity / weights[j]);
            capacity -= fraction * weights[j];
            j++;
        }
    }
    return j;
}

let capacity = 50;
let values = [60, 100, 120];
let weights = [10, 20, 30];
let n = values.length;

console.log(fractionKnapsack(capacity, weights, values, n));

详解:在分数骑士问题中,贪心算法选择单位价值最高的物品,如果完全装不下,则选择能够装入的最大部分。

3. Java - 活动选择问题

import java.util.Arrays;

public class ActivitySelection {
    public static int minActivities(int[][] activities) {
        Arrays.sort(activities, (a, b) -> a[0] - b[0]); // 按开始时间排序

        int count = 0;
        int end = -1;
        for (int[] activity : activities) {
            if (activity[0] > end) {
                // 当前活动与前一个活动不重叠
                count++;
                end = activity[1];
            }
        }
        return count;
    }

    public static void main(String[] args) {
        int[][] activities = {{1, 4}, {2, 5}, {3, 6}};
        System.out.println(minActivities(activities)); // 输出可以选择的最大活动数
    }
}

详解:在活动选择问题中,贪心算法选择结束时间最早的活动,然后继续选择与之不重叠的、开始时间最早的活动。

4. C++ - 图的最小生成树 - Kruskal算法

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int findParent(vector<int> &parent, int vertex) {
    if (parent[vertex] == vertex)
        return vertex;
    return parent[vertex] = findParent(parent, parent[vertex]);
}

void unionSets(vector<int> &parent, vector<int> &rank, int a, int b) {
    int aRoot = findParent(parent, a);
    int bRoot = findParent(parent, b);
    
    if (aRoot != bRoot) {
        if (rank[aRoot] < rank[bRoot])
            parent[aRoot] = bRoot;
        else if (rank[aRoot] > rank[bRoot])
            parent[bRoot] = aRoot;
        else {
            parent[bRoot] = aRoot;
            rank[aRoot]++;
        }
    }
}

int main() {
    vector<pair<int, int>> edges = {{0, 1, 4}, {0, 2, 8}, {1, 2, 11}, {1, 3, 7}, {2, 3, 9}};
    int V = 4;
    
    vector<int> parent(V, -1);
    vector<int> rank(V, 0);
    
    sort(edges.begin(), edges.end());
    
    for (auto &edge : edges) {
        int a = edge.first, b = edge.second, c = edge third;
        int aRoot = findParent(parent, a);
        int bRoot = findParent(parent, b);
        
        if (aRoot != bRoot) {
            unionSets(parent, rank, a, b);
            cout << a << " - " << b << " : " << c << endl;
        }
    }
    
    return 0;
}

详解:在Kruskal算法中,我们选择边权最小的边,但是要确保这条边不会形成环。贪心算法通过不断添加最小边直到所有顶点都被连接起来。

5. C# - 哈夫曼编码

using System;

class HuffmanCoding {
    class Node {
        public char ch;
        public int freq;
        public Node left, right;
        public Node(char ch, int freq) {
            this.ch = ch;
            this.freq = freq;
        }
    }
    
    Node CreateNode(char ch, int freq) {
        return new Node(ch, freq);
    }
    
    Node CreateTree(Frequency[] arr, int[] freq) {
        int start = 0;
        while (arr.Length != 1) {
            Array.Sort(arr, (x, y) => x.freq - y.freq);
            Node left = arr[start++];
            Node right = arr[start++];
            Node newNode = CreateNode('\0', left.freq + right.freq);
            newNode.left = left;
            newNode.right = right;
            arr[start++] = newNode;
        }
        return arr[0];
    }
    
    void PrintCodes(Node node, string str) {
        if (node != null) {
            if (node.left == null && node.right == null) {
                Console.WriteLine(node.ch + " : " + str);
            }
            PrintCodes(node.left, str + "0");
            PrintCodes(node.right, str + "1");
        }
    }
    
    public static void Main() {
        HuffmanCoding huffman = new HuffmanCoding();
        int[] freq = {10, 20, 30, 40, 50};
        Frequency[] arr = new Frequency[freq.Length];
        for (int i = 0; i < freq.Length; i++) {
            arr[i] = new Frequency('A' + i, freq[i]);
        }
        Node root = huffman.CreateTree(arr, freq);
        huffman.PrintCodes(root, "");
    }
}

class Frequency {
    public char ch;
    public int freq;
    public Frequency(char ch, int freq) {
        this.ch = ch;
        this.freq = freq;
    }
}

详解:哈夫曼编码是一种使用频率来构建最优二叉树的贪心算法,它为每个字符赋予一个唯一的变长编码,使得整体编码的平均长度最
小。

我是小王,希望分享的内容对您有帮助,谢谢关注和支持哦!