leetcode基础知识 -贪心算法

贪心算法铺垫

钞票支付问题

尽可能多的选择面值较大的钞票
在这里插入图片描述count // 程序运行中,用了钞票的总数量
/ 除法,默认丢弃小数部分
user // i面值 使用的数量
x 当前金额
RMB[i] * user i种面值使用的金额
在这里插入图片描述

贪心算法不成立情况

当前大面额的钞票,可以拆分成n个同样的小面额钞票
此时
当前最优解即为全局最优解

比如:增加7元面额钞票 凑14元
7 = 5+1+1 不满足拆成n个同样的小面额
此时14的最优解 7+7 ,但是贪心算法: 10+1+1+1+1

如何解决上述问题呢? - 动态规划
在这里插入图片描述

贪心习题

分糖果 - LC455

在这里插入图片描述
思路: 排序,选择更接近的需求的糖果给对应的孩子
更具象化贪心: 能用小糖果满足的 就不用大唐果

cookie 能满足child cookie,child++
cookie 不能满足child 丢弃这个考虑下一个,cookie++,

cookie == 0时候,糖果发完了,此时child数 ,就是能满足的孩子数

过程

在这里插入图片描述

代码实现
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
	int findContentChildren(vector<int>& g, vector<int>& s) {  //g child ; s cookie
		sort(g.begin(), g.end());
		sort(s.begin(), s.end());
		int cookie = 0;
		int child = 0;
		while (cookie < s.size() && child <g.size())
		{
			if (g[child] <= s[cookie])
			{
				child++;
			}
			cookie++;
		}
		return child;
	}
};

摇摆序列 - 相邻元素之差正负交替出现 -LC376

相邻元素之差 可以是 前减后,也可以是后减前
这里规定为后减前

摇摆序列定义:

数据小于2的序列,也被划分为摇摆序列
在这里插入图片描述

题目 - 求摇摆序列的最长子序列 (可以不连续)

在这里插入图片描述

思路

当遇到连续上升序列的时候 , 小 大(上升) 小
此时应该在上升序列中,选择尽可能大的数
这样的话 ,才能增加让后面小的那个数 成为摇摆位的可能

核心: 遇见连续上升/下降序列的时候 ,保留首尾
在这里插入图片描述状态机
当前元素nums[i] , 和上一元素nums[i-1]比较
nums[i] > nums[i-1] up
nums[i] < nums[i-1] down
在这里插入图片描述框架

#include <iostream>
#include <vector>
using namespace std;

class Solution {
public:
	int wiggleMaxLength(vector<int>& nums) {
		static const int BEGIN = 0;       //赋值是对应case 0 - case BEGIN
		static const int UP = 1;
		static const int DOWN = 2;
		int START = BEGIN;
		for (int i = 1; i < nums.size(); i++)
		{
			switch (START) {
			case BEGIN: {
				if () {
					//BEGIN状态转化为UP
				}

				else if ()
				{
					//BEGIN状态转化为DOWN
				}
				break;
			}
			case UP: {
				if () {
					//UP 转化为 DOWN
				}
				break;
			}
			case DOWN: {
				if () {
					//DOWN 转化为 UP
				}
				break;
			}
			}
		}
	}
};
代码
#include <iostream>
#include <vector>
using namespace std;

class Solution {
public:
	int wiggleMaxLength(vector<int>& nums) {
		if (nums.size() < 2)
		{
			return nums.size();
		}
		static const int BEGIN = 0;       //赋值是对应case 0 - case BEGIN
		static const int UP = 1;
		static const int DOWN = 2;
		int STATE = BEGIN;  //STATE状态
		int lengthmax = 1;
		for (int i = 1; i < nums.size(); i++)
		{
			switch (STATE) {
			case BEGIN: {
				if (nums[i] > nums[i-1]) {
					//BEGIN状态转化为UP
					STATE = UP;
					lengthmax++;
				}

				else if (nums[i] < nums[i-1])
				{
					//BEGIN状态转化为DOWN
					STATE = DOWN;
					lengthmax++;
				}
				break;
			}
			case UP: {
				if (nums[i] < nums[i - 1]) {
					//UP 转化为 DOWN
					STATE = DOWN;
					lengthmax++;
				}
				break;
			}
			case DOWN: {
				if (nums[i] > nums[i - 1]) {
					//DOWN 转化为 UP
					STATE = UP;
					lengthmax++;
				}
				break;
			}
			}
		}
		return lengthmax;
	}
};

移除K个数字后的最小值 -LC402

1432219 移除3个数字 剩下可能性:1432,1431,1438,4322…
在这里插入图片描述枚举: 枚举太多了 不考虑

思考过程

在这里插入图片描述在这里插入图片描述
让优先的最高位最小 - 删除较大的数字
去掉最高位 比较 :没去之前的最高位 ? 去掉之后的最高位
去掉次高位 比较:没去之前的次高位 ? 去掉之后的次高位

若确定去掉这一位,后面就不用看了
已经确定去掉后数会变小 ,去掉高位的时候 数变小了 一定是最小数

暴力解法 + 贪心 每次去1个,去K次

在这里插入图片描述

class Solution {
public:
	string removeKdigits(string num, int k) {
		if (num.size() == k)
		{
			return "0";
		}
		while (k--)
		{
			for (int i = 0; i < num.size(); i++)
			{
				if (num[i] > num[i + 1])
				{
					num.erase(num.begin()+i);	
					break;
				}
					
			}
		}
		while (num[0] == '0' && num.size() > 1)  //去掉首位的所有0
		{
			num.erase(num.begin());
		}
		return num;
	}
};
贪心+栈解法

判断前面的数字是否比后面的大, - 如果是,删除前面的数(较大的数)
转化为
后面的的数字比前面的小 - 新入栈的元素是否比栈顶小, 如果是,删除栈顶元素

// 也可以理解为 :栈顶元素比新进来的元素大,就出栈

后面的 -> 后面进来的数字 = 新入栈的元素
前面的 ->上一个数字 = 当前栈顶元素

新入栈的元素 > 当前栈顶元素 push进展
新入栈的元素 < 当前栈顶元素 pop弹栈 直到栈空或者k次操作完成

在这里插入图片描述

思考

在这里插入图片描述数字相当于从小到大 由栈底到栈顶排好后,k仍然>0 ,还得去掉k个数字, 那么弹出k个top即可

我的思路:
当去掉第1位数字后,2,3位可能为零, 所以当数字>1的时候,首位为零 去掉
更优解法:
当栈为空 数字为零的时候 不放进去不久好了,剩的我还后面删

//栈为空的时候  可以放入, 栈为0的时候 也可以放入, 
但是栈为空 数字为零  不可以放入
∴if( number!=0  || !mystack.empty() ) 
mystack.push(number)

因为是栈存储的,所以如果按顺序弹出首部元素 ,是逆序的
所以insert(result.begin(),mystack.top()+‘0’);
用头插法插入

代码

注意一个问题:
当top>number的时候, 弹出top,此时不是立刻进入number
而是比较新的top和number
直到找到一个top<number,此时才插入number

呈现出由栈底到栈顶,数字由小到大的栈

#include <string>
#include <stack>
using namespace std;
class Solution {
public:
	string removeKdigits(string num, int k) {
		stack<int> mystack;
		string result = "";
		if (num.size() == k)
		{
			return "0";
		}
		for (int i = 0; i < num.size(); i++)   
		{
			int number = num[i] - '0';
			//栈不为空,k>0   //栈顶元素>number, 此时出栈, 然后当前的top和number比较,直到找到一个比number小的数,number才放入
			while (!mystack.empty() && k > 0 &&  mystack.top() > number  )
			{
				mystack.pop();
				k--;
			}	
				mystack.push(number);
			
		}
		while (!mystack.empty() && k > 0)  //string扫完了(此时大的数字都在后面),但是k>0仍要去掉几个数
		{
			mystack.pop();
			k--;
		}
		for (int i = mystack.size(); i > 0; i--)  //stack ->string
		{
			result.insert(result.begin(), mystack.top() + '0');
			mystack.pop();
		}
		while (result[0] == '0' && result.size() > 1)  //去零
		{
			result.erase(result.begin());
		}
		return result;
	}
};
int main()
{
	Solution solu;
	solu.removeKdigits("100", 1);
}
进一步改进

删除result循环去掉首位0的操作
增加push时候的条件,
增加result为空的情况

#include <string>
#include <stack>
using namespace std;
class Solution {
public:
	string removeKdigits(string num, int k) {
		stack<int> mystack;
		string result = "";
		if (num.size() == k)
		{
			return "0";
		}
		for (int i = 0; i < num.size(); i++)   
		{
			int number = num[i] - '0';
			//栈不为空,k>0   //栈顶元素>number, 此时出栈, 然后当前的top和number比较,直到找到一个比number小的数,number才放入
			while (!mystack.empty() && k > 0 &&  mystack.top() > number  )
			{
				mystack.pop();
				k--;
			}	
			if (number != 0 || !mystack.empty())
			{
				mystack.push(number);
			}	
		}
		while (!mystack.empty() && k > 0)  //string扫完了(此时大的数字都在后面),但是k>0仍要去掉几个数
		{
			mystack.pop();
			k--;
		}
		for (int i = mystack.size(); i > 0; i--)  //stack ->string
		{
			result.insert(result.begin(), mystack.top() + '0');
			mystack.pop();
		}
		if (result == "")
		{
			result = "0";
		}
		return result;
	}
};
int main()
{
	Solution solu;
	solu.removeKdigits("100", 1);
}

跳跃游戏Jump Game -LC55

给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。


输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。

输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

思考

难点: 我不确定我每次可以跳几部,每次都是在一个集合里面选择一个合适的数字
比如: a[0] = 2; 难点就是 我是跳1步呢 还是跳2步呢?
在这里插入图片描述

分析
我的思路:
在没有超过目标之前,那么我每次都跳最远的那个数字,发现不符合
我就回退一个最远,这个时候看 怎么跳能达到目的,
如果我运气不好正好踩到一个0,怎么样都跳不到目的,那就再回退一个最远,再跳一个最远-1远,

正确解题:
引入数组maxindex[] , 里面存储当前索引所能跳转的最远距离
maxindex[i] = i+nums[i];

方法一:

最远到达距离 max_index - 我写的是arrived_max
在这里插入图片描述

#include <vector>
using namespace std;
class Solution {
public:
	bool canJump(vector<int>& nums) {
		vector <int> max_index;
		for (int i = 0; i < nums.size(); i++)
		{
			max_index.push_back(i + nums[i]);
			//max_index[i] = i + nums[i];
		}
		int jump = 0;
		int max_arrived = max_index[0]; //max_arrived 所能达到的最远索引
		while (jump < max_index.size() && jump <= max_arrived)  //用jump遍历max_index[]数组
		{
			if (max_arrived < max_index[jump]) 
			{
				max_arrived = max_index[jump];
			}	
			jump++;
		}
		if (jump == nums.size())
		{
			return true;
		}
		return false;
	}
};
方法二:

应该跳到nums[i]所能跳到的maxindex[i]最大的地方去 ,
这样跳的比单纯的跳到nums[i]的最大值还远,并且可以规避掉出现0的情况,好处多多

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值