这里写目录标题
贪心算法铺垫
钞票支付问题
尽可能多的选择面值较大的钞票
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的情况,好处多多