欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 金融 > 贪心算法一

贪心算法一

2025/3/11 1:21:19 来源:https://blog.csdn.net/AAlykk/article/details/144830211  浏览:    关键词:贪心算法一

> 作者:დ旧言~
> 座右铭:松树千年终是朽,槿花一日自为荣。

> 目标:了解什么是贪心算法,并且掌握贪心算法。

> 毒鸡汤:有些事情,总是不明白,所以我不会坚持。早安!

> 专栏选自:贪心算法_დ旧言~的博客-CSDN博客

> 望小伙伴们点赞👍收藏✨加关注哟💕💕

一、算法讲解

贪心算法的定义:

贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,只做出在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。

解题的一般步骤是:

  1. 建立数学模型来描述问题;
  2. 把求解的问题分成若干个子问题;
  3. 对每一子问题求解,得到子问题的局部最优解;
  4. 把子问题的局部最优解合成原来问题的一个解。

如果大家比较了解动态规划,就会发现它们之间的相似之处。最优解问题大部分都可以拆分成一个个的子问题,把解空间的遍历视作对子问题树的遍历,则以某种形式对树整个的遍历一遍就可以求出最优解,大部分情况下这是不可行的。贪心算法和动态规划本质上是对子问题树的一种修剪,两种算法要求问题都具有的一个性质就是子问题最优性(组成最优解的每一个子问题的解,对于这个子问题本身肯定也是最优的)。

动态规划方法代表了这一类问题的一般解法,我们自底向上构造子问题的解,对每一个子树的根,求出下面每一个叶子的值,并且以其中的最优值作为自身的值,其它的值舍弃。而贪心算法是动态规划方法的一个特例,可以证明每一个子树的根的值不取决于下面叶子的值,而只取决于当前问题的状况。换句话说,不需要知道一个节点所有子树的情况,就可以求出这个节点的值。由于贪心算法的这个特性,它对解空间树的遍历不需要自底向上,而只需要自根开始,选择最优的路,一直走到底就可以了。

二、算法习题


2.1、第一题

题目链接:860. 柠檬水找零 - 力扣(LeetCode)

题目描述:

算法思路:

a. 遇到 5 元钱,直接收下;

b. 遇到 10 元钱,找零 5 元钱之后,收下;

c. 遇到 20 元钱:

  1. 先尝试凑 10 + 5 的组合;
  2. 如果凑不出来,拼凑 5 + 5 + 5 的组合;

代码呈现:

class Solution {
public:bool lemonadeChange(vector<int>& bills) {int five = 0, ten = 0;for (auto x : bills) {if (x == 5)five++;       // 5 元:直接收下else if (x == 10) // 10 元:找零 5 元{if (five == 0)return false;five--;ten++;} else // 20 元:分情况讨论{if (ten && five) // 贪⼼{ten--;five--;} else if (five >= 3) {five -= 3;} elsereturn false;}}return true;}
};

2.2、第二题

题目链接:2208. 将数组和减半的最少操作次数 - 力扣(LeetCode)

题目描述:

算法思路:

  1. 每次挑选出「当前」数组中「最⼤」的数,然后「减半」;
  2. 直到数组和减少到⾄少⼀半为⽌。

为了「快速」挑选出数组中最⼤的数,我们可以利⽤「堆」这个数据结构。

代码呈现:

class Solution {
public:int halveArray(vector<int>& nums) {priority_queue<double> heap; // 创建⼀个⼤根堆double sum = 0.0;for (int x : nums) // 把元素都丢进堆中,并求出累加和{heap.push(x);sum += x;}sum /= 2.0; // 先算出⽬标和int count = 0;while (sum > 0) // 依次取出堆顶元素减半,直到减到之前的⼀半以下{double t = heap.top() / 2.0;heap.pop();sum -= t;count++;heap.push(t);}return count;}
};

2.3、第三题

题目链接:179. 最大数 - 力扣(LeetCode)

题目描述:

算法思路:

可以先优化:

将所有的数字当成字符串处理,那么两个数字之间的拼接操作以及⽐较操作就会很⽅便。

贪⼼策略:

按照题⽬的要求,重新定义⼀个新的排序规则,然后排序即可。

排序规则:

  1. 「A 拼接 B」 ⼤于 「B 拼接 A」,那么 A 在前,B 在后;
  2.  「A 拼接 B」 等于 「B 拼接 A」,那么 A B 的顺序⽆所谓;
  3. 「A 拼接 B」 ⼩于 「B 拼接 A」,那么 B 在前,A 在后

代码呈现:

class Solution {
public:string largestNumber(vector<int>& nums) {// 优化:把所有的数转化成字符串vector<string> strs;for (int x : nums)strs.push_back(to_string(x));// 排序sort(strs.begin(), strs.end(), [](const string& s1, const string& s2) {return s1 + s2 > s2 + s1;});// 提取结果string ret;for (auto& s : strs)ret += s;if (ret[0] == '0')return "0";return ret;}
};

2.4、第四题

题目链接:376. 摆动序列 - 力扣(LeetCode)

题目描述:

算法思路:

对于某⼀个位置来说:

  • 如果接下来呈现上升趋势的话,我们让其上升到波峰的位置;
  • 如果接下来呈现下降趋势的话,我们让其下降到波⾕的位置。
  • 因此,如果把整个数组放在「折线图」中,我们统计出所有的波峰以及波⾕的个数即可

代码呈现:

class Solution {
public:int wiggleMaxLength(vector<int>& nums) {int n = nums.size();if (n < 2)return n;int ret = 0, left = 0;for (int i = 0; i < n - 1; i++) {int right = nums[i + 1] - nums[i]; // 计算接下来的趋势if (right == 0)continue; // 如果⽔平,直接跳过if (right * left <= 0)ret++; // 累加波峰或者波⾕left = right;}return ret + 1;}
};

2.5、第五题

题目链接:300. 最长递增子序列 - 力扣(LeetCode)

题目描述:

算法思路:

  • 我们在考虑最⻓递增⼦序列的⻓度的时候,其实并不关⼼这个序列⻓什么样⼦,我们只是关⼼最后⼀个元素是谁。这样新来⼀个元素之后,我们就可以判断是否可以拼接到它的后⾯。
  • 因此,我们可以创建⼀个数组,统计⻓度为 x 的递增⼦序列中,最后⼀个元素是谁。为了尽可能让这个序列更⻓,我们仅需统计⻓度为 x 的所有递增序列中最后⼀个元素的「最⼩值」。
  • 统计的过程中发现,数组中的数呈现「递增」趋势,因此可以使⽤「⼆分」来查找插⼊位置。

代码呈现:

class Solution {
public:int lengthOfLIS(vector<int>& nums) {int n = nums.size();vector<int> ret;ret.push_back(nums[0]);for (int i = 1; i < n; i++) {if (nums[i] > ret.back()) // 如果能接在最后⼀个元素后⾯,直接放{ret.push_back(nums[i]);} else {// ⼆分插⼊位置int left = 0, right = ret.size() - 1;while (left < right) {int mid = (left + right) >> 1;if (ret[mid] < nums[i])left = mid + 1;elseright = mid;}ret[left] = nums[i]; // 放在 left 位置上}}return ret.size();}
};

2.6、第六题

题目链接:334. 递增的三元子序列 - 力扣(LeetCode)

题目描述:

算法思路:

不⽤⼀个数组存数据,仅需两个变量即可。也不⽤⼆分插⼊位置,仅需两次⽐较就可以找到插⼊位
置。

代码呈现:

class Solution {
public : bool increasingTriplet(vector<int>& nums) 
{int a = nums[0], b = INT_MAX;for (int i = 1; i < nums.size(); i++) {if (nums[i] > b)return true;else if (nums[i] > a)b = nums[i];elsea = nums[i];}return false;}
};

三、结束语 

今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词