[数据结构与算法]贪心算法(原理+代码)

简介: [数据结构与算法]贪心算法(原理+代码)

一、什么是贪心算法

贪心算法(Greedy Algorithm)是一种在每一步选择中都采取当前状态下最优解的策略,希望通过一系列局部最优的选择最终达到全局最优。贪心算法通常用于优化问题,其中在每个阶段都做出局部最优的选择,希望通过这种方式达到全局最优解。

贪心算法的主要特点是它对解的选择没有显式的规定,而是通过一系列的局部选择来达到整体最优。每一步都选择当前状态下的最优解,而不考虑未来的影响。

贪心算法的一般流程如下:

  1. 问题建模: 将问题抽象成一系列的局部最优选择。
  2. 选择策略: 确定每一步的选择策略,即如何在当前状态下做出最优的选择。
  3. 解决问题: 通过贪心策略逐步解决问题,直到达到全局最优解或者近似最优解。

虽然贪心算法在一些问题中非常有效,但并不是所有问题都适合使用贪心算法。在某些情况下,贪心算法可能无法得到全局最优解,因为它不进行回溯。因此,在使用贪心算法时,需要仔细分析问题的特性,确保贪心策略能够达到预期的最优解。典型的贪心算法应用包括最小生成树、单源最短路径、任务调度等。

二、常见应用算法

Prim算法:贪心算法的一种常见应用是Prim算法。Prim算法的基本思想是从一个初始顶点开始,每次选择一条边,将一个新的顶点纳入生成树中,直到所有的顶点都被纳入生成树。

#include <iostream>
#include <climits>
using namespace std;
 
#define V 5  // 顶点数
 
int minKey(int key[], bool mstSet[]) {
    int min = INT_MAX, min_index;
 
    for (int v = 0; v < V; v++) {
        if (!mstSet[v] && key[v] < min) {
            min = key[v];
            min_index = v;
        }
    }
 
    return min_index;
}
 
void printMST(int parent[], int graph[V][V]) {
    cout << "Minimum Spanning Tree (Prim):" << endl;
    for (int i = 1; i < V; i++)
        cout << "Edge: " << parent[i] << " - " << i << " Weight: " << graph[i][parent[i]] << endl;
}
 
void primMST(int graph[V][V]) {
    int parent[V];
    int key[V];
    bool mstSet[V];
 
    // 初始化所有键值为无穷大,都未包含在生成树中
    for (int i = 0; i < V; i++) {
        key[i] = INT_MAX;
        mstSet[i] = false;
    }
 
    // 选择第一个顶点作为起始点
    key[0] = 0;
    parent[0] = -1;
 
    for (int count = 0; count < V - 1; count++) {
        int u = minKey(key, mstSet);
 
        mstSet[u] = true;
 
        for (int v = 0; v < V; v++) {
            if (graph[u][v] && !mstSet[v] && graph[u][v] < key[v]) {
                parent[v] = u;
                key[v] = graph[u][v];
            }
        }
    }
 
    printMST(parent, graph);
}
 
int main() {
    int graph[V][V] = { {0, 2, 0, 6, 0},
                        {2, 0, 3, 8, 5},
                        {0, 3, 0, 0, 7},
                        {6, 8, 0, 0, 9},
                        {0, 5, 7, 9, 0} };
 
    primMST(graph);
 
    return 0;
}

执行结果:

活动选择问题(Activity Selection Problem):

假设有一个教室,需要安排一系列活动,每个活动都有一个开始时间和结束时间。活动之间不能重叠,即同一时间教室只能进行一个活动目标是选择尽可能多的活动使得它们不会相互冲突,即在给定时间内进行尽可能多的非重叠活动。

C++代码:

#include <iostream>
#include <algorithm>
#include <vector>
 
using namespace std;
 
// 首先声明一个活动结构体,包含活动开始时间和结束时间两个变量
struct Activity {
    int start, finish;
};
 
// 按照活动结束时间升序排序的比较函数
bool compare(Activity a, Activity b) {
    return (a.finish < b.finish);
}
 
// 贪心算法解决活动选择问题
void printMaxActivities(Activity activities[], int n) {
    // 按照结束时间升序排序
    sort(activities, activities + n, compare);
 
    cout << "Selected Activities:\n";
 
    // 第一个活动总是被选中
    int i = 0;
    cout << "(" << activities[i].start << ", " << activities[i].finish << "), ";
 
    // 遍历剩余活动
    for (int j = 1; j < n; j++) {
        // 如果当前活动的开始时间大于等于上一个选中活动的结束时间,选择当前活动
        if (activities[j].start >= activities[i].finish) {
            cout << "(" << activities[j].start << ", " << activities[j].finish << "), ";
            i = j;
        }
    }
}
 
int main() {
    // 示例活动数据(活动开始实践和结束时间)
    Activity activities[] = {{1, 2}, {3, 4}, {0, 6}, {5, 7}, {8, 9}, {5, 9}};
    int n = sizeof(activities) / sizeof(activities[0]);
 
    // 调用贪心算法解决活动选择问题
    printMaxActivities(activities, n);
 
    return 0;
}

活动按照结束时间升序排序,然后使用贪心算法选择尽可能多的不重叠活动。这个算法的时间复杂度为O(n log n),其中n是活动的数量。

执行结果:

三、小结:

1. 基本思想:

  • 贪心算法是一种在每一步选择中都采取当前状态下最优解的策略,希望通过一系列局部最优的选择达到全局最优。
  • 贪心策略通常不进行回溯,一旦做出选择就不再改变。

2. 适用条件:

  • 问题具有最优子结构性质:问题的最优解可以通过子问题的最优解推导得到。
  • 贪心选择性质:每一步的选择都是当前状态下的最优解,即局部最优。

3. 过程步骤:

  • 建模: 将问题抽象成一系列局部最优的选择。
  • 选择策略: 确定每一步的选择策略,即如何在当前状态下做出最优的选择。
  • 解决问题: 通过贪心策略逐步解决问题,直到达到全局最优解或者近似最优解。

4. 优缺点:

  • 优点: 算法简单、高效,适用于一些问题,尤其是最优子结构和贪心选择性质明显的情况。
  • 缺点: 不适用于所有问题,可能得不到全局最优解,只能得到局部最优解或者近似最优解。

5. 注意事项:

  • 贪心算法的适用性需要仔细分析问题的性质,确保贪心策略能够达到预期的最优解。
  • 在一些问题中,贪心算法可以作为求解问题的一部分,而不是整个问题的解决方案。

最后欢迎三连点赞、关注、收藏哦!


相关文章
|
5天前
|
机器学习/深度学习 数据采集 算法
短视频到底如何推荐的?深度剖析视频算法推送原理详细且专业的解读-优雅草卓伊凡-【01】短视频算法推荐之数据收集
短视频到底如何推荐的?深度剖析视频算法推送原理详细且专业的解读-优雅草卓伊凡-【01】短视频算法推荐之数据收集
47 12
短视频到底如何推荐的?深度剖析视频算法推送原理详细且专业的解读-优雅草卓伊凡-【01】短视频算法推荐之数据收集
|
8天前
|
JavaScript 前端开发 算法
JavaScript 中通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能,JS中排序算法的使用详解(附实际应用代码)
Array.sort() 是一个功能强大的方法,通过自定义的比较函数,可以处理各种复杂的排序逻辑。无论是简单的数字排序,还是多字段、嵌套对象、分组排序等高级应用,Array.sort() 都能胜任。同时,通过性能优化技巧(如映射排序)和结合其他数组方法(如 reduce),Array.sort() 可以用来实现高效的数据处理逻辑。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
1月前
|
机器学习/深度学习 存储 算法
近端策略优化(PPO)算法的理论基础与PyTorch代码详解
近端策略优化(PPO)是深度强化学习中高效的策略优化方法,广泛应用于大语言模型的RLHF训练。PPO通过引入策略更新约束机制,平衡了更新幅度,提升了训练稳定性。其核心思想是在优势演员-评论家方法的基础上,采用裁剪和非裁剪项组成的替代目标函数,限制策略比率在[1-ϵ, 1+ϵ]区间内,防止过大的策略更新。本文详细探讨了PPO的基本原理、损失函数设计及PyTorch实现流程,提供了完整的代码示例。
324 10
近端策略优化(PPO)算法的理论基础与PyTorch代码详解
|
1月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
95 29
|
23天前
|
运维 NoSQL 算法
【📕分布式锁通关指南 04】redis分布式锁的细节问题以及RedLock算法原理
本文深入探讨了基于Redis实现分布式锁时遇到的细节问题及解决方案。首先,针对锁续期问题,提出了通过独立服务、获取锁进程自己续期和异步线程三种方式,并详细介绍了如何利用Lua脚本和守护线程实现自动续期。接着,解决了锁阻塞问题,引入了带超时时间的`tryLock`机制,确保在高并发场景下不会无限等待锁。最后,作为知识扩展,讲解了RedLock算法原理及其在实际业务中的局限性。文章强调,在并发量不高的场景中手写分布式锁可行,但推荐使用更成熟的Redisson框架来实现分布式锁,以保证系统的稳定性和可靠性。
44 0
【📕分布式锁通关指南 04】redis分布式锁的细节问题以及RedLock算法原理
|
1月前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
120 25
|
1月前
|
存储 人工智能 算法
C 408—《数据结构》算法题基础篇—数组(通俗易懂)
408考研——《数据结构》算法题基础篇之数组。(408算法题的入门)
79 23
|
2月前
|
机器学习/深度学习 算法 PyTorch
深度强化学习中SAC算法:数学原理、网络架构及其PyTorch实现
软演员-评论家算法(Soft Actor-Critic, SAC)是深度强化学习领域的重要进展,基于最大熵框架优化策略,在探索与利用之间实现动态平衡。SAC通过双Q网络设计和自适应温度参数,提升了训练稳定性和样本效率。本文详细解析了SAC的数学原理、网络架构及PyTorch实现,涵盖演员网络的动作采样与对数概率计算、评论家网络的Q值估计及其损失函数,并介绍了完整的SAC智能体实现流程。SAC在连续动作空间中表现出色,具有高样本效率和稳定的训练过程,适合实际应用场景。
443 7
深度强化学习中SAC算法:数学原理、网络架构及其PyTorch实现
|
3月前
|
算法 Java 数据库
理解CAS算法原理
CAS(Compare and Swap,比较并交换)是一种无锁算法,用于实现多线程环境下的原子操作。它通过比较内存中的值与预期值是否相同来决定是否进行更新。JDK 5引入了基于CAS的乐观锁机制,替代了传统的synchronized独占锁,提升了并发性能。然而,CAS存在ABA问题、循环时间长开销大和只能保证单个共享变量原子性等缺点。为解决这些问题,可以使用版本号机制、合并多个变量或引入pause指令优化CPU执行效率。CAS广泛应用于JDK的原子类中,如AtomicInteger.incrementAndGet(),利用底层Unsafe库实现高效的无锁自增操作。
130 0
理解CAS算法原理
|
2月前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
66 2

热门文章

最新文章

  • 1
    局域网屏幕监控系统中的Python数据结构与算法实现
    140
  • 2
    2024重生之回溯数据结构与算法系列学习之串(12)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】
    58
  • 3
    2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
    52
  • 4
    2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
    55
  • 5
    2024重生之回溯数据结构与算法系列学习(8)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
    75
  • 6
    2024重生之回溯数据结构与算法系列学习之王道第2.3章节之线性表精题汇总二(5)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
    44
  • 7
    2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
    80
  • 8
    2024重生之回溯数据结构与算法系列学习之顺序表习题精讲【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
    46
  • 9
    2024重生之回溯数据结构与算法系列学习之顺序表【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
    53
  • 10
    2024重生之回溯数据结构与算法系列学习【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
    54