贪心算法

本文介绍了贪心算法在解决LeetCode问题中的应用,包括分发饼干、摇摆序列、移除K位数字、跳跃游戏系列以及射击气球等经典问题的解题思路和代码实现,探讨如何用最少的操作达成目标。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、分发饼干(LeetCode 455)

题目:
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
注意:
你可以假设胃口值为正。
一个小朋友最多只能拥有一块饼干。
思路:
在这里插入图片描述
代码:

class Solution
{
public: 
	int findcontentchildren(vector<int>& g, vector<int>& s)
	{
		sort(g.begin(),g.end());//将满足因子按照从小到大的顺序排列
		sort(s.begin(),s.end());//将饼干数目按从小到大的顺序排列
		int child=0;//初始孩子个数为零
		int candy=0;//糖果也为零
		while(child < g.size() && candy < s.size())//当饼干数目和满足因子都没有遍历完的情况下才能继续遍历
		{
			if(g[child] <= s[candy])//如果满足因子小于饼干数目,则可以满足,孩子数加1
			{
				child++;
			}
			candy++;//无论能不能满足糖果都加1
		}
		return child;
	}
};

一、摇摆序列(LeetCode 376)

题目
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。

例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。
思路
在这里插入图片描述在这里插入图片描述
代码

#include <iostream>
#include <vector>
#include <stdlib.h>
using namespace std;
class Solution
{
public:
	int wiggleMaxLenth(vector<int>& nums)
	{
		if(nums.size() < 2)//若数字个数小于2,最长摇摆序列即为数组大小
		{
			return nums.size();
		}
		static const int BEGIN=0;//初始状态
		static const int UP = 1;//上升状态
		static const int DOWN = 2;//下降状态
		int STATE = BEGIN;
		int max_length = 1;

		for(int i=1;i<nums.size();i++)//遍历数组中的所有数字,从第二个数字开始遍历
		{
			switch(STATE)    
			{
				case BEGIN://初始状态
					if(nums[i-1] < nums[i])//如果第i个数字大于第i-1个数字即数组为递增,max_length增加一个
					//若是判断i和i+1,会出现漏判和指针错误等情况!!!
					{
						STATE = UP;
						max_length++;
					}
					else if(nums[i] < nums[i-1])//如果第i个数字小于第i-1个数字即数组为递减,max_length增加一个
					{
						STATE = DOWN;
						max_length++;
					}
					break;
				case UP://数组为递增状态
					if(nums[i-1] > nums[i]) //如果出现第i个数字小于第i-1个数字则说明数列开始摇摆max_length增加一个
					{
						STATE = DOWN;
						max_length++;
					}
					break;
				case DOWN://数组为递减状态
					if(nums[i-1] < nums[i])//如果出现第i个数字大于第i-1个数字则说明数列开始摇摆max_length增加一个
					{
						STATE = UP;
						max_length++;
					}
					break;
			}
		}
		return max_length;
	}
};
int main()
{
	vector<int> nums;
	nums.push_back(1);
	nums.push_back(17);
	nums.push_back(5);
	nums.push_back(10);
	nums.push_back(13);
	nums.push_back(15);
	nums.push_back(10);
	nums.push_back(5);
	nums.push_back(16);
	nums.push_back(8);
	Solution solve;
	cout<<solve.wiggleMaxLenth(nums)<<endl;
	system("pause");
	return 0;
}

三、移除K位数字(LeetCode 402)

题目
给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。
注意:
num 的长度小于 10002 且 ≥ k。
num 不会包含任何前导零。
思路:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
特殊处理:
在这里插入图片描述
代码:

#include <stdlib.h>
#include <vector>
#include <string>
#include <iostream>
using namespace std;

class Solution
{
public:
	string removeKdigits(string num, int k)
	{
		vector<int> S;//可用于数字遍历
		string result = "";//初始结果值为空,不要输空格空格也是字符!!!
		for(int i=0;i<num.length();i++){//遍历所有字符
			int number = num[i]-'0';   //将字符转化为整型
			while(!S.empty() && S[S.size()-1]>number && k>0){//如果S不为空且S中最后一个数字大于新的数字且还可以减少数字
				S.pop_back();       //弹出栈中最后一个数字,减少数字的机会减1
				k--;
			}
			if(number !=0 || !S.empty()){//如果数字不为零或者栈不为空则将数字push进栈(避免0成为第一个数字,并且非第一位的0可以push进去)
				S.push_back(number);
			}
		}
		while(!S.empty() && k>0){//如果栈不为空且还有减少数字的机会,将栈顶的数字弹出(此种情况说明前面的数字(高位)都比后面的(低位)数字小)
			S.pop_back();
			k--;//机会数减1
		}
		for(int i=0; i<S.size();i++){//循环将数字转化成字符添加到rusult中
			result.append(1,'0'+S[i]);
		}
		if(result == ""){//如果result为空则返回0
			result ="0";
		}
		return result;
	}
};

int main()
{
	Solution solve;
	string result = solve.removeKdigits("112", 1);
	cout<<result<<endl;
	system("pause");
	return 0;
}

四-1 跳跃游戏 (LeetCode 55)

题目:
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
思路:
在这里插入图片描述
代码:

#include <stdlib.h>
#include <iostream>
#include <vector>

using namespace std;

class Solution{
public:
	bool canjump(vector<int>& nums){
		vector<int> index;   //用来存储可到达的最远位置
		for(int i=0;i<nums.size();i++){//遍历数字nums求每个位置可到达的最远位置
			index.push_back(nums[i]+i);
		}
		int max_index=index[0];//初始最大跳跃位置为index[0]
		int jump=0;//初始jump为零
		while(jump<nums.size()&&jump<=max_index){//只要jump小于nums的大小以及小于max_index则加1
			if(max_index<index[jump]){   //其中如果max_index小于现在位置可跳的最远位置时则更新
				max_index=index[jump];
			}
			jump++;
		}
		if(jump == nums.size()){//如果jump等于nums的大小则可以跳到最后
			return true;
		}
		return false;
	}
};

int main()
{
	vector<int> nums;
	nums.push_back(2);
	nums.push_back(3);
	nums.push_back(1);
	nums.push_back(1);
	nums.push_back(4);
	Solution solve;
	cout<<solve.canjump(nums)<<endl;
	system("pause");
	return 0;
}

四-2 跳跃游戏2 (LeetCode 45)

题目:
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
思路:
在这里插入图片描述
在这里插入图片描述

int jump(vector<int>& nums){
		if(nums.size()<2){//元素少于两个则不用跳
			return 0;
		}
		int current_max_index = nums[0];//目前可以跳的最远位置
		int pre_max_max_index = nums[0];//用来存储现在位置和目前可跳最远位置之间可跳的最大位置的值
		int min_jump = 1;//至少跳一步
		for(int i=0;i<nums.size();i++){
			if(i>current_max_index){//如果i>目前可以跳的最远位置,current_max_index更新min_jump加1
				current_max_index = pre_max_max_index;
				min_jump++;
			}
			if(pre_max_max_index < nums[i]+i){//发现有大于pre_max_max_index 的更新
				pre_max_max_index = nums[i]+i;
			}
		}
		return min_jump;
	}

五、射击气球 (LeetCode 452)

题目:
在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在104个气球。

一支弓箭可以沿着x轴从不同点完全垂直地射出。在坐标x处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。
思路:
在这里插入图片描述
代码:

#include <stdlib.h>
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
bool cmp(const pair<int, int>& a,const pair<int, int>& b){//用于比较气球左端点的大小
	return a.first < b.first;
}

class Solution{
public:
	int findArrowshots(vector<pair<int, int>>& points){
		if(points.size()==0)//如果points为空则返回0
            return 0;
		sort(points.begin(),points.end(),cmp);//将points中数字对根据左坐标左坐标按从小到大排序

		int shoot_num = 1;//初始箭数为1
		int shoot_begin = points[0].first;//初始设计区间为第一个气球所在的坐标区间
		int shoot_end = points[0].second;
		for(int i=0;i<points.size();i++){//遍历所有区间
			if(points[i].first <= shoot_end){//如果气球i的左坐标不超过射击区间的右坐标,则可以进行射击区间的更新
				shoot_begin = points[i].first;//初始左坐标为新气球的左坐标
				if(shoot_end > points[i].second){//如果射击区间的右坐标大于新气球的右坐标则射击区间的右坐标为新气球的右坐标
					shoot_end = points[i].second;
				}
			}
			else{//否则射击数目加1,新的气球所在区间为新的射击区间
				shoot_num++;
				shoot_begin = points[i].first;
				shoot_end = points[i].second;
			}
		}
		return shoot_num;
	}
};

int main()
{
	vector<pair<int, int>> points;
	points.push_back(make_pair(10,16));
	points.push_back(make_pair(2,8));
	points.push_back(make_pair(1,6));
	points.push_back(make_pair(7,12));
	Solution solve;
	cout<<solve.findArrowshots(points)<<endl;
	system("pause");
	return 0;
}

六、最优加油方法

题目:
在这里插入图片描述
思路:
在这里插入图片描述
代码:

#include <stdlib.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>

using namespace std;
bool cmp(const pair<int, int>& a,const pair<int, int>& b){//用于比较第一个数字大小
	return a.first > b.first;
}
class Solution{
public:
	int get_mini_stop(int L, int P, vector<pair<int, int>>& stop){//stop第一个值存到终点的距离,第二个值存油量
		std::priority_queue<int> Q;//最大堆存储油量
		int result = 0;//加油次数
		stop.push_back(make_pair(0,0));//将终点添加至stop
		sort(stop.begin(), stop.end(), cmp);//按到终点距离从大到小排列

		for(int i=0; i < stop.size();i++){
			int dis = L - stop[i].first;//当前剩下要走的距离为初始距离减下一点要走的距离
			while(P < dis && !Q.empty()){//当汽油量小于当前要走的距离时,且到达过加油地点
				P = P + Q.top();//加最多的油量
				Q.pop();
				result++;
			}
			if(P < dis && Q.empty()){//如果油加完还小的话则不能到达终点
				return -1;
			}
			P=P-dis;//油量要减去走过的距离
			L = stop[i].first;//距离更新
			Q.push(stop[i].second);//将汽油量放进最大对你
		}
		return result;
	}
};

int main(){
	vector<pair<int,int>> stop;
	int L=25;
	int P=10;
	stop.push_back(make_pair(4,4));
    stop.push_back(make_pair(5,2));
	stop.push_back(make_pair(11,5));
	stop.push_back(make_pair(15,10));
	Solution solve;

	cout<<"至少要加"<<solve.get_mini_stop(L,P,stop)<<"次油"<<endl;
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值