贪心算法:活动选择问题与霍夫曼编码

引言

贪心算法是一种在每一步选择中都采取当前最优解的方法。贪心算法通常用于解决一些优化问题,它的主要优势在于其简单性和高效性。本文将详细介绍两个经典的贪心算法问题:活动选择问题和霍夫曼编码。

目录

  1. 活动选择问题
  2. 霍夫曼编码

活动选择问题

定义

活动选择问题是一个经典的贪心算法问题,目的是在给定的一组活动中选择尽可能多的活动,使得这些活动互不重叠。

问题描述

假设有一组活动 (A = {a_1, a_2, \ldots, a_n}),每个活动 (a_i) 都有一个开始时间 (s_i) 和一个结束时间 (f_i)。活动选择问题要求选择尽可能多的活动,使得这些活动互不重叠,即对于任意两个选择的活动 (a_i) 和 (a_j),有 (f_i \leq s_j) 或 (f_j \leq s_i)。

贪心选择策略

贪心选择策略是每次选择结束时间最早且不与已选择活动重叠的活动。

示例

假设有以下活动:

活动     开始时间  结束时间
a1       1        4
a2       3        5
a3       0        6
a4       5        7
a5       3        9
a6       5        9
a7       6        10
a8       8        11
a9       8        12
a10      2        14
a11      12       16

贪心算法实现

下面是用Java实现活动选择问题的代码示例:

java 复制代码
import java.util.*;

public class ActivitySelection {
    // 活动类,包含开始时间和结束时间
    static class Activity {
        int start;
        int finish;

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

    // 活动选择算法
    public static void selectActivities(List<Activity> activities) {
        // 按结束时间升序排序
        activities.sort(Comparator.comparingInt(a -> a.finish));

        System.out.println("选中的活动:");

        // 第一个活动总是被选中
        Activity lastSelected = activities.get(0);
        System.out.println("活动 (" + lastSelected.start + ", " + lastSelected.finish + ")");

        // 选择其他活动
        for (int i = 1; i < activities.size(); i++) {
            Activity currentActivity = activities.get(i);
            if (currentActivity.start >= lastSelected.finish) {
                System.out.println("活动 (" + currentActivity.start + ", " + currentActivity.finish + ")");
                lastSelected = currentActivity;
            }
        }
    }

    public static void main(String[] args) {
        List<Activity> activities = Arrays.asList(
                new Activity(1, 4),
                new Activity(3, 5),
                new Activity(0, 6),
                new Activity(5, 7),
                new Activity(3, 9),
                new Activity(5, 9),
                new Activity(6, 10),
                new Activity(8, 11),
                new Activity(8, 12),
                new Activity(2, 14),
                new Activity(12, 16)
        );

        selectActivities(activities);
    }
}

活动选择问题的详细图解

以下是活动选择问题的执行过程图解:
选择活动过程 活动 排序后的活动 活动a1 1, 4 活动a4 5, 7 活动a8 8, 11 活动a11 12, 16 活动a1 1, 4 活动a2 3, 5 活动a3 0, 6 活动a4 5, 7 活动a5 3, 9 活动a6 5, 9 活动a7 6, 10 活动a8 8, 11 活动a9 8, 12 活动a10 2, 14 活动a11 12, 16 活动选择问题 按结束时间升序排序活动 选择第一个活动 选择其他活动 选择结束

霍夫曼编码

定义

霍夫曼编码(Huffman Coding)是一种用于数据压缩的贪心算法。通过构建最优前缀码,使得出现频率较高的字符使用较短的编码,而出现频率较低的字符使用较长的编码,从而达到压缩数据的目的。

问题描述

给定一个字符集及其对应的权重(出现频率),通过霍夫曼编码生成最优前缀码,使得编码后的总长度最短。

贪心选择策略

贪心选择策略是每次选择权重最小的两个节点,合并它们,并将新节点的权重设为两个节点权重之和。

示例

假设有以下字符及其对应的权重:

字符    权重
A       5
B       9
C       12
D       13
E       16
F       45

霍夫曼编码算法实现

下面是用Java实现霍夫曼编码的代码示例:

java 复制代码
import java.util.PriorityQueue;

public class HuffmanCoding {
    // 节点类,包含字符、权重、左子节点和右子节点
    static class Node implements Comparable<Node> {
        char character;
        int frequency;
        Node left;
        Node right;

        Node(char character, int frequency) {
            this.character = character;
            this.frequency = frequency;
        }

        Node(int frequency, Node left, Node right) {
            this.frequency = frequency;
            this.left = left;
            this.right = right;
        }

        @Override
        public int compareTo(Node o) {
            return this.frequency - o.frequency;
        }
    }

    // 构建霍夫曼树
    public static Node buildHuffmanTree(char[] characters, int[] frequencies) {
        PriorityQueue<Node> priorityQueue = new PriorityQueue<>();
        for (int i = 0; i < characters.length; i++) {
            priorityQueue.add(new Node(characters[i], frequencies[i]));
        }

        while (priorityQueue.size() > 1) {
            Node left = priorityQueue.poll();
            Node right = priorityQueue.poll();
            Node newNode = new Node(left.frequency + right.frequency, left, right);
            priorityQueue.add(newNode);
        }

        return priorityQueue.poll();
    }

    // 打印霍夫曼编码
    public static void printHuffmanCodes(Node root, String code) {
        if (root == null) {
            return;
        }

        if (root.character != '\0') {
            System.out.println(root.character + ": " + code);
        }

        printHuffmanCodes(root.left, code + "0");
        printHuffmanCodes(root.right, code + "1");
    }

    public static void main(String[] args) {
        char[] characters = {'A', 'B', 'C', 'D', 'E', 'F'};
        int[] frequencies = {5, 9, 12, 13, 16, 45};

        Node root = buildHuffmanTree(characters, frequencies);
        printHuffmanCodes(root, "");
    }
}

霍夫曼编码的详细图解

以下是霍夫曼编码问题的执行过程图解:
霍夫曼编码生成 合并过程 字符及权重 根节点: ABCDEF 100 左子节点: ABCDE 55 右子节点: F 45 左子节点: AB 14 右子节点: CD 25 左子节点: A 5 右子节点: B 9 左子节点: C 12 右子节点: D 13 生成编码 打印霍夫曼编码 初始队列 A 5, B 9, C 12, D 13, E 16, F 45 合并 A 5 和 B 9 生成新节点 AB 14 更新队列 队列: AB 14, C 12, D 13, E 16, F 45 合并 C 12 和 D 13 生成新节点 CD 25 更新队列 队列: AB 14, CD 25, E 16, F 45 合并 AB 14 和 E 16 生成新节点 ABE 30 更新队列 队列: ABE 30, CD 25, F 45 合并 ABE 30 和 CD 25 生成新节点 ABCDE 55 更新队列 队列: ABCDE 55, F 45 合并 ABCDE 55 和 F 45 生成根节点 ABCDEF 100 A 5 B 9 C 12 D 13 E 16 F 45 霍夫曼编码 初始化优先队列 将每个字符和权重加入优先队列 选择两个权重最小的节点 合并两个节点并加入新节点到优先队列 重复选择和合并直到优先队列只剩一个节点 生成霍夫曼树 生成霍夫曼编码 结束

结论

通过上述讲解和实例代码,我们详细展示了贪心算法在活动选择问题和霍夫曼编码中的应用。贪心算法是一种简单且高效的算法,适用于许多优化问题。希望这篇博客对您有所帮助!


如果您觉得这篇文章对您有帮助,请关注我的CSDN博客,点赞并收藏这篇文章,您的支持是我持续创作的动力!


关键内容总结

  • 活动选择问题的定义和实现
  • 霍夫曼编码的定义和实现
  • 两种贪心算法的应用示例及详细图解

推荐阅读:深入探索设计模式专栏 ,详细讲解各种设计模式的应用和优化。点击查看:深入探索设计模式


特别推荐:设计模式实战专栏 ,深入解析设计模式的实际应用,提升您的编程技巧。点击查看:设计模式实战

如有任何疑问或建议,欢迎在评论区留言讨论。谢谢阅读!

相关推荐
cwj&xyp9 分钟前
Python(二)str、list、tuple、dict、set
前端·python·算法
xiaoshiguang34 小时前
LeetCode:222.完全二叉树节点的数量
算法·leetcode
爱吃西瓜的小菜鸡4 小时前
【C语言】判断回文
c语言·学习·算法
别NULL4 小时前
机试题——疯长的草
数据结构·c++·算法
TT哇5 小时前
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
java·算法·蓝桥杯
yuanbenshidiaos6 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习6 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA6 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
chengooooooo6 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc6 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法