• 深度剖析贪心算法:原理、优势与实战


    概述

    贪心算法是一种通过每一步的局部最优选择来寻找整体最优解的方法。在每个步骤中,贪心算法选择当前状态下的最佳选项,而不考虑未来可能的影响。尽管它不能保证一定能找到全局最优解,但贪心算法通常简单且高效,适用于许多实际问题。

    核心原理

    贪心算法是一种寻找全局最优解的方法,其核心原理可概括为以下步骤:

    1. 问题建模:将问题分解成一系列子问题,每个子问题都有一定的优先级。

    2. 选择策略:在每个步骤中,选择当前子问题的最佳选项,即局部最优解,而不考虑未来可能的影响。

    3. 更新状态:根据所选策略,更新问题的状态以反映已经做出的选择。

    4. 重复:反复执行步骤2和步骤3,直到达到问题的终止条件。

    优势

    贪心算法具有以下优势:

    • 高效性:贪心算法通常具有较低的时间复杂度,适用于大规模问题。
    • 简单性:相对于某些复杂的动态规划算法,贪心算法的实现相对简单。
    • 实用性:贪心算法适用于许多实际问题,特别是那些具有贪心选择性质的问题。

    实际应用

    贪心算法的应用非常广泛,其中包括以下一些常见问题

    1、背包问题

    问题描述:给定一组物品,每个物品有自己的重量和价值,以及一个容量有限的背包。目标是选择哪些物品放入背包,以使得背包中的物品总价值最大。

    问题描述使用贪心算法,每次选择单位价值最高的物品放入背包,直到背包装满或所有物品都被考虑过。

    问题描述:

    1. 首先,我们将物品按照单位重量的价值(价值与重量的比率)从高到低排序,以便优先选择单位价值最高的物品。
    2. 然后,我们依次考虑排序后的物品,尝试将每个物品放入背包中,直到背包装满或者物品已经全部考虑完。
    3. 如果当前物品的重量小于等于背包的剩余容量,我们将其完全放入背包,并累加相应的价值。
    4. 如果当前物品的重量大于背包的剩余容量,我们将部分放入背包,以充分利用背包容量,然后退出循环,因为无法再放入更多的物品。

    Python 示例

    1. def knapsack(values, weights, capacity):
    2. """
    3. 背包问题:选择不同物品以获得最大价值
    4. Args:
    5. values (List[int]): 物品的价值列表
    6. weights (List[int]): 物品的重量列表
    7. capacity (int): 背包的容量限制
    8. Returns:
    9. int: 背包问题的最大价值
    10. """
    11. n = len(values)
    12. items = [(values[i], weights[i]) for i in range(n)]
    13. items.sort(key=lambda x: x[1] / x[0], reverse=True)
    14. total_value = 0
    15. for item in items:
    16. if capacity >= item[1]:
    17. total_value += item[0]
    18. capacity -= item[1]
    19. else:
    20. total_value += item[0] * (capacity / item[1])
    21. break
    22. return total_value
    23. # 示例数据
    24. values = [10, 20, 30]
    25. weights = [2, 5, 10]
    26. capacity = 15
    27. result = knapsack(values, weights, capacity)
    28. print("背包问题最大价值:", result)
    29. # 运行结果
    30. 背包问题最大价值: 60.0

    Java 示例

    1. public int knapsack(int[] values, int[] weights, int capacity) {
    2. /**
    3. * 背包问题:选择不同物品以获得最大价值
    4. *
    5. * @param values 物品的价值数组
    6. * @param weights 物品的重量数组
    7. * @param capacity 背包的容量限制
    8. * @return 背包问题的最大价值
    9. */
    10. int n = values.length;
    11. Item[] items = new Item[n];
    12. for (int i = 0; i < n; i++) {
    13. items[i] = new Item(values[i], weights[i]);
    14. }
    15. Arrays.sort(items, (a, b) -> Double.compare(b.valuePerWeight, a.valuePerWeight));
    16. int totalValue = 0;
    17. for (Item item : items) {
    18. if (capacity >= item.weight) {
    19. totalValue += item.value;
    20. capacity -= item.weight;
    21. } else {
    22. totalValue += item.value * ((double) capacity / item.weight);
    23. break;
    24. }
    25. }
    26. return totalValue;
    27. }
    28. // 示例数据
    29. public static void main(String[] args) {
    30. int[] values = {10, 20, 30};
    31. int[] weights = {2, 5, 10};
    32. int capacity = 15;
    33. int result = knapsack(values, weights, capacity);
    34. System.out.println("背包问题最大价值: " + result);
    35. }
    36. // 运行结果
    37. 背包问题最大价值: 60

    2、最小生成树问题

    问题描述:给定一个带权无向图,要找到一棵生成树,使得包含所有顶点且总权值最小。

    解决方法:使用Kruskal算法,按照边的权重从小到大逐步构建生成树,确保生成树不形成环。

    思路说明:

    1. 我们首先将图的边按权重从小到大进行排序。
    2. 然后从最小权重的边开始,逐个考虑这些边。
    3. 对于每条边,如果它连接的两个顶点不在同一个连通分量中,我们就将这条边添加到生成树中,并将连接的顶点合并到一个连通分量中。
    4. 重复上述步骤,直到生成树包含所有的顶点,或者所有边都已考虑完。

    Python 示例

    1. class Graph:
    2. def __init__(self, vertices):
    3. """
    4. 最小生成树问题:使用Kruskal算法找到最小生成树
    5. Args:
    6. vertices (int): 图中的顶点数
    7. """
    8. self.V = vertices
    9. self.graph = []
    10. def add_edge(self, u, v, w):
    11. """
    12. 添加一条边到图
    13. Args:
    14. u (int): 边的起始顶点
    15. v (int): 边的结束顶点
    16. w (int): 边的权重
    17. """
    18. self.graph.append([u, v, w])
    19. def kruskal_mst(self):
    20. """
    21. 使用Kruskal算法找到最小生成树
    22. Returns:
    23. int: 最小生成树的总权值
    24. List[List[int]]: 最小生成树的边列表
    25. """
    26. result = []
    27. self.graph = sorted(self.graph, key=lambda item: item[2])
    28. parent = list(range(self.V))
    29. def find(i):
    30. if parent[i] != i:
    31. parent[i] = find(parent[i])
    32. return parent[i]
    33. def union(i, j):
    34. root_i = find(i)
    35. root_j = find(j)
    36. if root_i != root_j:
    37. parent[root_i] = root_j
    38. for u, v, w in self.graph:
    39. if find(u) != find(v):
    40. result.append([u, v, w])
    41. union(u, v)
    42. minimum_cost = sum(edge[2] for edge in result)
    43. return minimum_cost, result
    44. # 示例数据
    45. g = Graph(4)
    46. g.add_edge(0, 1, 10)
    47. g.add_edge(0, 2, 6)
    48. g.add_edge(0, 3, 5)
    49. g.add_edge(1, 3, 15)
    50. g.add_edge(2, 3, 4)
    51. minimum_cost, min_spanning_tree = g.kruskal_mst()
    52. print("最小生成树的总权值:", minimum_cost)
    53. print("最小生成树的边:", min_spanning_tree)
    54. # 运行结果
    55. 最小生成树的总权值: 19
    56. 最小生成树的边: [[2, 3, 4], [0, 3, 5], [0, 1, 10]]

    Java 示例

    1. import java.util.*;
    2. class Graph {
    3. private int V, E;
    4. private List edges;
    5. static class Edge {
    6. int src, dest, weight;
    7. Edge(int src, int dest, int weight) {
    8. this.src = src;
    9. this.dest = dest;
    10. this.weight = weight;
    11. }
    12. }
    13. Graph(int V, int E) {
    14. this.V = V;
    15. this.E = E;
    16. edges = new ArrayList<>();
    17. }
    18. void addEdge(int src, int dest, int weight) {
    19. edges.add(new Edge(src, dest, weight));
    20. }
    21. int[] kruskalMST() {
    22. int result = 0;
    23. edges.sort(Comparator.comparingInt(e -> e.weight));
    24. int[] parent = new int[V];
    25. Arrays.fill(parent, -1);
    26. int edgeCount = 0;
    27. List selectedEdges = new ArrayList<>();
    28. for (Edge edge : edges) {
    29. int srcParent = find(parent, edge.src);
    30. int destParent = find(parent, edge.dest);
    31. if (srcParent != destParent) {
    32. result += edge.weight;
    33. parent[srcParent] = destParent;
    34. selectedEdges.add(edge);
    35. edgeCount++;
    36. }
    37. if (edgeCount == V - 1) break;
    38. }
    39. return new int[]{result, selectedEdges.size()};
    40. }
    41. private int find(int[] parent, int node) {
    42. if (parent[node] == -1) return node;
    43. return find(parent, parent[node]);
    44. }
    45. }
    46. // 示例数据
    47. public static void main(String[] args) {
    48. Graph graph = new Graph(4);
    49. graph.addEdge(0, 1, 10);
    50. graph.addEdge(0, 2, 6);
    51. graph.addEdge(0, 3, 5);
    52. graph.addEdge(1, 3, 15);
    53. graph.addEdge(2, 3, 4);
    54. int[] result = graph.kruskalMST();
    55. System.out.println("最小生成树的总权值: " + result[0]);
    56. System.out.println("最小生成树的边数: " + result[1]);
    57. }
    58. // 运行结果
    59. 最小生成树的总权值: 19
    60. 最小生成树的边数: 3

    3、Huffman编码

    问题描述:给定一组字符及其出现频率,要构建一种前缀编码,使得高频字符具有较短的编码。

    解决方法:使用贪心算法,构建Huffman树,通过对字符进行合并,生成前缀编码,确保高频字符具有较短的编码。

    思路说明:

    1. 首先,我们需要构建Huffman树,这可以通过使用一个优先队列(最小堆)来实现。我们将字符的频率作为优先级,较低频率的字符排在前面。
    2. 从队列中取出两个频率最低的节点,将它们合并为一个新节点,新节点的频率为两者之和,然后将新节点重新放入队列中。
    3. 重复上述步骤,直到队列中只剩下一个节点,这个节点就是Huffman树的根节点。
    4. 构建Huffman树后,我们可以通过遍历树来生成字符的编码。从根节点开始,向左走表示添加'0',向右走表示添加'1',直到达到叶子节点,即可得到字符的编码。

    Python 示例

    1. import heapq
    2. from collections import defaultdict
    3. class HuffmanNode:
    4. def __init__(self, char, freq):
    5. self.char = char
    6. self.freq = freq
    7. self.left = None
    8. self.right = None
    9. def __lt__(self, other):
    10. return self.freq < other.freq
    11. def build_huffman_tree(chars, freq):
    12. """
    13. 构建Huffman树
    14. Args:
    15. chars (List[str]): 字符列表
    16. freq (List[int]): 字符对应的频率列表
    17. Returns:
    18. HuffmanNode: Huffman树的根节点
    19. """
    20. heap = [HuffmanNode(char, freq) for char, freq in zip(chars, freq)]
    21. heapq.heapify(heap)
    22. while len(heap) > 1:
    23. left = heapq.heappop(heap)
    24. right = heapq.heappop(heap)
    25. merged = HuffmanNode('$', left.freq + right.freq)
    26. merged.left = left
    27. merged.right = right
    28. heapq.heappush(heap, merged)
    29. return heap[0]
    30. def huffman_codes(root):
    31. """
    32. 生成Huffman编码
    33. Args:
    34. root (HuffmanNode): Huffman树的根节点
    35. Returns:
    36. dict: 字符到编码的映射
    37. """
    38. codes = {}
    39. def generate_codes(node, code=""):
    40. if node is None:
    41. return
    42. if node.char != '$':
    43. codes[node.char] = code
    44. generate_codes(node.left, code + "0")
    45. generate_codes(node.right, code + "1")
    46. generate_codes(root)
    47. return codes
    48. # 示例数据
    49. chars = ['a', 'b', 'c', 'd', 'e', 'f']
    50. freq = [5, 9, 12, 13, 16, 45]
    51. root = build_huffman_tree(chars, freq)
    52. codes = huffman_codes(root)
    53. for char, code in codes.items():
    54. print(f"Character: {char}, Code: {code}")
    55. # 运行结果
    56. Character: a, Code: 110
    57. Character: b, Code: 111
    58. Character: c, Code: 100
    59. Character: d, Code: 101
    60. Character: e, Code: 0
    61. Character: f, Code: 10

    Java 示例

    1. import java.util.*;
    2. class HuffmanNode {
    3. char data;
    4. int frequency;
    5. HuffmanNode left, right;
    6. HuffmanNode(char data, int frequency) {
    7. this.data = data;
    8. this.frequency = frequency;
    9. left = right = null;
    10. }
    11. }
    12. public class HuffmanCoding {
    13. public static void main(String[] args) {
    14. char[] chars = {'a', 'b', 'c', 'd', 'e', 'f'};
    15. int[] freq = {5, 9, 12, 13, 16, 45};
    16. HuffmanNode root = buildHuffmanTree(chars, freq);
    17. Map codes = huffmanCodes(root);
    18. for (Map.Entry entry : codes.entrySet()) {
    19. System.out.println("Character: " + entry.getKey() + ", Code: " + entry.getValue());
    20. }
    21. }
    22. public static HuffmanNode buildHuffmanTree(char[] chars, int[] freq) {
    23. PriorityQueue queue = new PriorityQueue<>(Comparator.comparingInt(node -> node.frequency));
    24. for (int i = 0; i < chars.length; i++) {
    25. queue.add(new HuffmanNode(chars[i], freq[i]));
    26. }
    27. while (queue.size() > 1) {
    28. HuffmanNode left = queue.poll();
    29. HuffmanNode right = queue.poll();
    30. HuffmanNode newNode = new HuffmanNode('$', left.frequency + right.frequency);
    31. newNode.left = left;
    32. newNode.right = right;
    33. queue.add(newNode);
    34. }
    35. return queue.poll();
    36. }
    37. public static Map huffmanCodes(HuffmanNode root) {
    38. Map codes = new HashMap<>();
    39. generateCodes(root, "", codes);
    40. return codes;
    41. }
    42. private static void generateCodes(HuffmanNode node, String code, Map codes) {
    43. if (node == null) {
    44. return;
    45. }
    46. if (node.data != '$') {
    47. codes.put(node.data, code);
    48. }
    49. generateCodes(node.left, code + "0", codes);
    50. generateCodes(node.right, code + "1", codes);
    51. }
    52. }
    53. // 示例数据
    54. public static void main(String[] args) {
    55. char[] chars = {'a', 'b', 'c', 'd', 'e', 'f'};
    56. int[] freq = {5, 9, 12, 13, 16, 45};
    57. HuffmanNode root = buildHuffmanTree(chars, freq);
    58. Map codes = huffmanCodes(root);
    59. for (Map.Entry entry : codes.entrySet()) {
    60. System.out.println("Character: " + entry.getKey() + ", Code: " + entry.getValue());
    61. }
    62. }
    63. // 运行结果
    64. Character: e, Code: 0
    65. Character: b, Code: 10
    66. Character: a, Code: 110
    67. Character: d, Code: 111
    68. Character: c, Code: 100
    69. Character: f, Code: 101

  • 相关阅读:
    STM32物联网项目-通过ESP12S模块连接TCP服务器
    Elasticsearch近实时架构
    AM@导数和微分的应用@弧微分和曲率
    向毕业妥协系列之深度学习笔记(一)浅层神经网络
    AOP的切入点Pointcut中的execution表达式详解
    2023-10-10 mysql-{mysql_rm_db}-失败后回滚-记录
    html5——前端笔记
    Archimedean property
    [go学习笔记.第十一章.项目案例] 2.客户信息管理系统
    【Java】过滤器和拦截器区别
  • 原文地址:https://blog.csdn.net/m0_54958293/article/details/133044799