开宗明义:本系列基于小象学院林沐老师课程《面试算法 LeetCode 刷题班》,刷题小白,旨在理解和交流,重在记录,望各位大牛指点!
Leetcode学习之贪心算法(1)
1、贪心法找钱
贪心法:遵循某种规律,不断贪心的选取当前最优策略的算法设计方法。
成立条件:当前最优解为全局最优解。
问题描述:有1元、2元、5元、10元、20元、50元、100元的钞票有无穷张。现使用这些钞票支付x元,最少需要多少张?
测试代码:
#include <stdio.h>
#include <iostream>
int main() {
const int RMB[] = { 100,50,20,10,5,2,1 };//人民币面额,这边需要排序,从大到小
const int num = 7;//7种面额
int x = 628;//用人民币最少多少张,组成x?
int count = 0;
for (int i = 0; i < num; i++) {
int use = x / RMB[i];//需要面额为RMB[i]的use张
count += use;//总计增加use张
x = x - RMB[i] * use;//将总额减去使用RMB[i]已组成的金额
printf("需要面额为%d的%d张,", RMB[i], use);
printf("剩余需要支付RMB :%d\n", x);
}
printf("总共需要 %d 张", count);
system("pause");
return 0;
}
效果图:
2、分糖果(排序、贪心) Leetcode 455.
题目来源:
L
e
e
t
c
o
d
e
455.
A
s
s
i
g
n
C
o
o
k
i
e
s
Leetcode \ 455.\ Assign \ Cookies
Leetcode 455. Assign Cookies
题目描述:已知一些孩子和一些糖果,每个孩子的需求因子为
g
g
g,每个有个大小
s
s
s。当某个糖果的大小
s
s
s>=某个孩子的需求因子
g
g
g的时候,代表该糖果可以满足孩子,求这些糖果,最多能满足几个孩子?(每个孩子最多只能用一个糖果满足)
思路:
- 当某个孩子可以被多个糖果满足时,是否优先使用某个糖果满足孩子?
- 当某个糖果可以满足多个孩子,是否需要优先满足某个孩子?
所以某个孩子可以用小的糖果满足,则没必要用更大的糖果满足!所以优先从需求因子小的孩子尝试!
步骤:
- 对需求因子数组 g g g与糖果大小数组 s s s进行从小到大的排序;
- 按照从小到大的顺序使用糖果是否满足某个孩子,每个糖果只尝试一次;尝试成功,则换下一个孩子尝试,直到没有更多的孩子或者没有更多的糖果,循环结束(无论失败或者成功,每个糖果只尝试一次,cookie向后移动)。
测试代码:
#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int> & s) {
sort(g.begin(), g.end());//对孩子的需求因子g与糖果大小s两组数进行排序
sort(s.begin(), s.end());
int child = 0;
int cookie = 0;//child代表已经满足了几个孩子。cookle代表尝试了几个糖果
while (child < g.size() && cookie < s.size())//当孩子的满足因子和糖果的大小都没有尝试完
{
if (g[child] < s[cookie]) {
child++;//糖果满足了孩子,孩子的child指针向后移动
}
cookie++;//无论失败或者成功,每个糖果只尝试一次,cookie向后移动
}
return child;//最终child即为满足的孩子个数
}
};
int main() {
Solution solve;
vector<int> g;
vector<int> s;
g.push_back(5);
g.push_back(10);
g.push_back(2);
g.push_back(9);
g.push_back(15);
g.push_back(9);
s.push_back(6);
s.push_back(1);
s.push_back(20);
s.push_back(3);
s.push_back(8);
printf("%d\n", solve.findContentChildren(g, s));
system("pause");
return 0;
}
效果图:
3、摇摆序列(贪心) Leetcode 376.
题目来源:
L
e
e
t
c
o
d
e
376.
W
i
g
g
l
e
S
u
b
s
e
q
u
e
n
c
e
Leetcode \ 376. \ Wiggle \ Subsequence
Leetcode 376. Wiggle Subsequence
题目描述:一个整数序列,如果两个相邻元素的差恰好正负(负正)交替出现,则该序列称为摇摆序列。一个小于2个序列直接称为摇摆序列。给一个随机序列,求这个序列满足摇摆序列定义的最长子序列长度。
要求描述:
分析:
思路:当序列有一段连续的递增或者递减时,为形成摇摆子序列,保留这段连续的递增或递减的首尾元素,这样更可能使得尾部后一个元素成为摇摆序列的下一个元素。
测试代码:
#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
int wiggleMaxLength(vector<int> &nums) {
if (nums.size() < 2) {
return nums.size();//序列个数小于2时,直接为摇摆序列
}
static const int begin = 0;
static const int up = 1;
static const int down = 2;//扫描时三种状态
int state = begin;
int max_length = 1;//摇摆序列的最大长度至少为1
//从第二个元素开始扫描
for (int i = 1; i < nums.size(); i++) {
switch (state) {
case begin:
if (nums[i] > nums[i - 1]) {
state = up;
max_length++;
}
else if(nums[i-1]>nums[i]){
state = down;
max_length++;
}
break;
case up:
if (nums[i - 1] > nums[i]) {
state = down;
max_length++;
}
break;
case down:
if (nums[i - 1] < nums[i]) {
state = up;
max_length++;
}
break;
}
}
return max_length;
}
};
int main() {
Solution solve;
vector<int> g;
g.push_back(1);
g.push_back(17);
g.push_back(5);
g.push_back(10);
g.push_back(13);
g.push_back(15);
g.push_back(10);
g.push_back(5);
g.push_back(16);
g.push_back(8);
printf("%d\n", solve.wiggleMaxLength(g));
system("pause");
return 0;
}
效果图:
4、移除K个数字(栈、贪心) Leetcode 402.
题目来源:
L
e
e
t
c
o
d
e
402.
R
e
m
o
v
e
K
D
i
g
i
t
s
Leetcode \ 402. \ Remove \ K \ Digits
Leetcode 402. Remove K Digits
题目描述:已知一个使用字符串表示的非负整数num,将num中的k个数字移除,求移除这k个数字后,可以获得最小的可能的新数字。
要求描述: 输入的num不会以任何数量的0字符开头,且字符长度<=10002,字符长度大于k。
思路:
使用栈存储最终结果或者删除工作,从高位向低位遍历num,如果遍历的数字大于栈顶元素,则将该数字push入栈,如果小于栈顶元素,则将原来的栈顶元素pop出栈,直到栈为空或者不能再删除数字(k=0)或者栈顶元素小于当前元素为止。
思考:
测试代码:
#include <stdio.h>
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Solution {
public:
string removeKdigits(string num, int k) {
vector<int> s;//使用vector作为栈,因为vector可以遍历
string result = "";//存储最终结果的字符串
for (int i = 0; i < num.length(); i++) {
int number = num[i] - '0';//将字符型num转化成整数使用
while (s.size() != 0 && s[s.size() - 1] > number && k > 0) {//当栈不空且栈顶元素大于数number且仍然可以删除数字
s.pop_back();//弹出栈顶元素
k--;
}
if (number != 0 || s.size() != 0) { //往栈里添元素,如100
s.push_back(number);//将数字number压入栈
}
}
while (s.size() != 0 && k > 0) {//如果栈不为空且仍可以删数字
s.pop_back();
k--;
}
for (int i = 0; i < s.size(); i++) {
result.append(1, '0' + s[i]);//整数转字符
}
if (result == "") {
result = "0";//如果result为空,result为0
}
return result;
}
};
int main() {
Solution solve;
string result = solve.removeKdigits("1432219", 3);
printf("%s\n", result.c_str());
string result2 = solve.removeKdigits("100200", 1);
printf("%s\n", result2.c_str());
system("pause");
return 0;
}
效果图: