当前位置: 代码网 > it编程>软件设计>算法 > 算法设计与分析——贪心算法(详解版)

算法设计与分析——贪心算法(详解版)

2024年07月28日 算法 我要评论
贪心算法通过每一步的局部最优选择,逐步构建全局最优解。虽然贪心算法不能保证在所有情况下都能找到最优解,但在许多经典问题中,它都能提供一个高效的解决方案。希望这些能对刚学习算法的同学们提供些帮助哦!!!

目录

🍉贪心算法

🍈介绍

🍈贪心算法的基本原理

🍈步骤

🍍选择初始状态

🍍应用贪心选择性

🍍检查算法的可行性

🍍更新状态

🍍结束条件

🍉经典问题示例

🍈活动选择问题

🍍问题描述

🍍问题分析

🍌解题思路

🍌解题步骤

🍌画图解析

🍍代码实现

🍌源代码

🍌运行结果

🍍效率

🍌时间复杂度

🍈哈夫曼编码

🍍问题描述

🍍问题分析

🍌解题思路

🍌解题步骤

🍌画图解析

🍍代码实现

🍌源代码(c++)

🍌代码解析

🍌运行结果

🍍效率

🍌时间复杂度

🍌空间复杂度

🍉贪心算法的优缺点

🍈优点🍈

🍈缺点🍈

🍈适用问题的特性🍈

🍈示例🍈

🍉总结


🍉贪心算法

🍈介绍

🍈贪心算法的基本原理

🍈步骤

🍍选择初始状态

  • 确定算法的起点。

🍍应用贪心选择性

  • 在当前状态下选择一个局部最优的动作,以期望能达到全局最优。

🍍检查算法的可行性

  • 判断当前选择是否满足问题的约束条件。

🍍更新状态

  • 根据贪心选择的结果更新当前状态。

🍍结束条件

  • 检查是否达到算法的结束条件。

🍉经典问题示例

🍈活动选择问题

🍍问题描述

🍍问题分析

🍌解题思路
  • 每次选择结束时间最早且与已选择活动不冲突的活动。选择的贪心策略是优先选择结束时间最早的活动。
🍌解题步骤
  1. 将活动按照结束时间从小到大排序。
  2. 初始化:选择第一个活动,并将其添加到结果集中。
  3. 对于每个后续活动,若其开始时间不早于上一个选择的活动的结束时间,则选择该活动。
  4. 返回结果集中的活动数。
🍌画图解析
活动  :  a1   a2   a3   a4   a5   a6
开始时间:  1    2    4    1    5    8
结束时间:  3    5    7    8    9   10

排序后活动:  a1  a2  a3  a5  a6  a4
选择顺序:  a1  a3  a6

🍍代码实现

🍌源代码
#include <iostream>
#include <vector>
#include <algorithm>

// 活动结构体
struct activity {
    int start;
    int end;
    int index; // 活动的原始索引
};

// 比较器函数,用于按结束时间排序活动
bool compare(activity a, activity b) {
    return a.end < b.end;
}

// 贪心算法选择最大数量的非重叠活动
std::vector<int> select_activities(std::vector<activity>& activities) {
    // 按结束时间排序活动
    std::sort(activities.begin(), activities.end(), compare);

    std::vector<int> selected_activities;
    int last_end_time = 0;

    for (const auto& activity : activities) {
        if (activity.start >= last_end_time) {
            selected_activities.push_back(activity.index);
            last_end_time = activity.end;
        }
    }

    return selected_activities;
}

int main() {
    // 定义活动的开始和结束时间
    std::vector<activity> activities = {
        {1, 3, 1},
        {2, 5, 2},
        {4, 7, 3},
        {1, 8, 4},
        {5, 9, 5},
        {8, 10, 6}
    };

    // 选择活动
    std::vector<int> selected_activities = select_activities(activities);

    // 打印选择的活动
    std::cout << "选择的活动顺序: ";
    for (int index : selected_activities) {
        std::cout << "a" << index << " ";
    }
    std::cout << std::endl;

    return 0;
}

🍌代码解析

🍌运行结果

🍍效率

🍌时间复杂度

因此,总的时间复杂度为:o(nlogn)+o(n)=o(nlogn)

🍌空间复杂度

因此,总的空间复杂度为:o(n)+o(logn)+o(n)=o(n)


🍈哈夫曼编码

🍍问题描述

🍍问题分析

🍌解题思路
  • 每次从频率表中选择频率最小的两个字符合并,形成一个新的字符,并更新频率表。重复此操作直到所有字符都被合并。
🍌解题步骤
  1. 构建一个优先队列,将所有字符及其频率放入队列。
  2. 从队列中取出频率最小的两个节点,将其合并为一个新的节点,并将合并后的节点重新加入队列。
  3. 重复步骤2,直到队列中只剩下一个节点,该节点即为哈夫曼树的根节点。
  4. 通过遍历哈夫曼树生成哈夫曼编码。
🍌画图解析
字符及频率:  a:45, b:13, c:12, d:16, e:9, f:5

步骤 1: 取频率最小的 'f' 和 'e'
        5    9
        |    |
        f    e

步骤 2: 合并 'f' 和 'e'
        14
       /  \
      f    e

步骤 3: 取频率最小的 'c' 和 'b'
        12   13
        |    |
        c    b

步骤 4: 合并 'c' 和 'b'
        25
       /  \
      c    b

步骤 5: 取频率最小的 '14' 和 'd'
        14   16
       /  \   |
      f    e  d

步骤 6: 合并 '14' 和 'd'
        30
       /  \
      14   d
     /  \
    f    e

步骤 7: 取频率最小的 '25' 和 '30'
        30    25
       /  \  /  \
      14   d c    b
     /  \
    f    e

步骤 8: 合并 '25' 和 '30'
        55
       /  \
      30   25
     /  \  /  \
    14   d c    b
   /  \
  f    e

🍍代码实现

🍌源代码(c++)
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
#include <algorithm>

// 节点结构体
struct node {
    char character;
    int frequency;
    node *left, *right;

    node(char ch, int freq, node* l = nullptr, node* r = nullptr)
        : character(ch), frequency(freq), left(l), right(r) {}
};

// 比较器,用于优先队列
struct compare {
    bool operator()(node* l, node* r) {
        return l->frequency > r->frequency;
    }
};

// 递归生成编码
void generate_codes(node* root, const std::string& str, std::unordered_map<char, std::string>& huffman_codes) {
    if (!root) return;

    if (!root->left && !root->right) {
        huffman_codes[root->character] = str;
    }

    generate_codes(root->left, str + "0", huffman_codes);
    generate_codes(root->right, str + "1", huffman_codes);
}

// 哈夫曼编码主函数
std::unordered_map<char, std::string> huffman_encoding(const std::unordered_map<char, int>& frequencies) {
    std::priority_queue<node*, std::vector<node*>, compare> heap;

    for (const auto& pair : frequencies) {
        heap.push(new node(pair.first, pair.second));
    }

    while (heap.size() > 1) {
        node* left = heap.top(); heap.pop();
        node* right = heap.top(); heap.pop();

        int sum = left->frequency + right->frequency;
        heap.push(new node('\0', sum, left, right));
    }

    node* root = heap.top();
    std::unordered_map<char, std::string> huffman_codes;
    generate_codes(root, "", huffman_codes);

    return huffman_codes;
}

// 打印哈夫曼编码
void print_huffman_codes(const std::unordered_map<char, std::string>& huffman_codes) {
    for (const auto& pair : huffman_codes) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }
}

// 测试哈夫曼编码
int main() {
    std::unordered_map<char, int> frequencies = { {'a', 45}, {'b', 13}, {'c', 12}, {'d', 16}, {'e', 9}, {'f', 5} };
    std::unordered_map<char, std::string> huffman_codes = huffman_encoding(frequencies);
    print_huffman_codes(huffman_codes);

    return 0;
}
🍌代码解析
🍌运行结果

🍍效率

🍌时间复杂度

结合上述各步骤,哈夫曼编码算法的总体时间复杂度为:o(nlogn)

🍌空间复杂度

结合上述各项,哈夫曼编码算法的总体空间复杂度为:o(n)


🍉贪心算法的优缺点

🍈优点🍈

🍈缺点🍈

🍈适用问题的特性🍈

为了确保贪心算法能够找到问题的最优解,需要满足以下特性

🍈示例🍈

🍉总结


希望这些能对刚学习算法的同学们提供些帮助哦!!!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com