刷题笔记
- 贪心算法leetcode专栏
- leetcode 455 分法饼干
- leetcode 376 摆动序列
- leetcode 53 最大子数组和
- leetcode 122 买卖股票的最佳时机 II
- leetcode 55 跳跃游戏
- leetcode 45 跳跃游戏 II
- leetcode 1005 K次取反后最大化的数组和
- leetcode 134 加油站
- leetcode 135 分发糖果
- leetcode 860 柠檬水找零
- leetcode 406 根据身高重建队列
- leetcode 452 用最少数量的箭引爆气球
- leetcode 435 无重叠区间
- leetcode 763 划分字母区间
- leetcode 56 合并区间
- leetcode 738 单调递增的数字
- leetcode 968 监控二叉树
贪心算法leetcode专栏
leetcode 455 分法饼干
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 cookie = 0;
while(child < g.size() && cookie < s.size())
{
if(g[child] <= s[cookie])
child++;
cookie++;
}
return child;
}
};
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 cookie = s.size()-1;
for(int i = g.size()-1; i >= 0; i--)
{
if(cookie >= 0 && s[cookie] >= g[i])
{
child++; //一个饼干用来满足一个孩子
cookie--;
}
}
return child;
}
};
leetcode 376 摆动序列
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
if (nums.size() <= 1) return nums.size();
int curDiff = 0; // 当前一对差值
int preDiff = 0; // 前一对差值
int result = 1; // 记录峰值个数,序列默认序列最右边有一个峰值
for (int i = 0; i < nums.size() - 1; i++) {
curDiff = nums[i + 1] - nums[i];
// 出现峰值
if ((curDiff > 0 && preDiff <= 0) || (preDiff >= 0 && curDiff < 0)) {
result++;
preDiff = curDiff;
}
}
return result;
}
};
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
if (nums.size() <= 1) return nums.size();
static const int BEGIN = 0;
static const int UP = 1;
static const int DOWN = 2;
int STATE = BEGIN;
int maxLen = 1;
int i = 0;
for(int i = 0; i < nums.size()-1; i++)
{
switch(STATE)
{
case BEGIN:
if(nums[i+1] > nums[i])
{
maxLen++;
STATE = UP;
}
if(nums[i+1] < nums[i])
{
maxLen++;
STATE = DOWN;
}
break;
case UP:
if(nums[i+1] < nums[i])
{
maxLen++;
STATE = DOWN;
}
break;
case DOWN:
if(nums[i+1] > nums[i])
{
maxLen++;
STATE = UP;
}
break;
}
}
return maxLen;
}
};
leetcode 53 最大子数组和
暴力搜索的结果会超时的
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int max = INT_MIN;
int sum;
for(int i = 0; i < nums.size(); i++)
{
sum = 0;
for(int j = i; j < nums.size(); j++)
{
sum = sum + nums[j];
max = sum > max ? sum : max;
}
}
return max;
}
};
局部最优:当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小。
全局最优:选取最大“连续和”
局部最优的情况下,并记录最大的“连续和”,可以推出全局最优。
从代码角度上来讲:遍历nums,从头开始用count累积,如果sum一旦加上nums[i]变为负数,那么就应该从nums[i+1]开始从0累积sum了,因为已经变为负数的sum,只会拖累总和。
这相当于是暴力解法中的不断调整最大子序和区间的起始位置。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int max = INT_MIN;
int sum = 0;
for(int i = 0; i < nums.size(); i++)
{
sum = sum + nums[i];
max = sum > max ? sum : max;
if(sum <= 0) sum = 0;
}
return max;
}
};
leetcode 122 买卖股票的最佳时机 II
class Solution {
public:
int maxProfit(vector<int>& prices) {
int result = 0;
for(int i = 1; i < prices.size(); i++)
{
result += max(prices[i] - prices[i-1], 0);
}
return result;
}
};
leetcode 55 跳跃游戏
class Solution {
public:
bool canJump(vector<int>& nums) {
if (nums.size() <= 1) return true;
int nowPos = 0;
int range = 0;
while (nowPos < nums.size()) {
range = nums[nowPos];
if (nowPos + range >= nums.size() - 1) { //可以跳到终点 terminate
return true;
}
if (range == 0 && nowPos < nums.size() - 1) { //不可能跳到终点
return false;
}
int NextPosMaxRange = INT_MIN;
int nextPos;
//nextPos必须在当前nowPos+range范围内选取i + nums[i]最大的那个记录NextPosMaxRange,也就是下次站在Pos = i位置时,是局部范围内可以跳的最远的选择。
for (int i = nowPos + 1; i <= nowPos + range; i++) {
if (i + nums[i] >= NextPosMaxRange) {
NextPosMaxRange = i + nums[i];
nextPos = i;
}
}
nowPos = nextPos;
}
return false;
}
};
class Solution {
public:
bool canJump(vector<int>& nums) {
int cover = 0; //cover是可以跳跃的最远的覆盖范围
if (nums.size() == 1) return true;
for (int i = 0; i <= cover; i++)
{
cover = max(i + nums[i], cover);
if (cover >= nums.size() - 1)
return true;
}
return false;
}
};
leetcode 45 跳跃游戏 II
class Solution {
public:
int jump(vector<int>& nums) {
if(nums.size() == 1) return 0; //跳0步,直接就是终点
int nowPos = 0;
int range = 0;
int cnt = 0;
while (nowPos < nums.size()) {
range = nums[nowPos];
if (nowPos + range >= nums.size() - 1) { //可以跳到终点 terminate
cnt++;
return cnt;
}
int NextPosMaxRange = INT_MIN;
int nextPos;
//nextPos必须在当前nowPos+range范围内选取i + nums[i]最大的那个记录NextPosMaxRange,也就是下次站在Pos = i位置时,是局部范围内可以跳的最远的选择。
for (int i = nowPos + 1; i <= nowPos + range; i++) {
if (i + nums[i] >= NextPosMaxRange) {
NextPosMaxRange = i + nums[i];
nextPos = i;
}
}
nowPos = nextPos;
cnt++;
}
return cnt;
}
};
leetcode 1005 K次取反后最大化的数组和
按逻辑写通过的
class Solution {
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
sort(nums.begin(), nums.end());
for(int i = 0; i < nums.size() || k > 0; i++)
{
if(k == 0)
{
break;
}
if(i >= nums.size() && k > 0)
{
goto Label;
}
if(nums[i] <= 0)
{
nums[i] = -nums[i];
k--;
}
else
{
Label:
sort(nums.begin(), nums.end());
nums[0] = -nums[0];
k--;
}
}
int sum = 0;
for(int i = 0; i < nums.size(); i++)
{
sum += nums[i];
}
return sum;
}
};
class Solution {
public:
static bool cmp(int a, int b)
{
return abs(a) > abs(b);
}
int largestSumAfterKNegations(vector<int>& nums, int k) {
sort(nums.begin(), nums.end(), cmp);
for(int i = 0; i < nums.size(); i++)
{
if(nums[i] <= 0 && k > 0)
{
nums[i] = -nums[i];
k--;
}
}
while(k)
{
nums[nums.size()-1] *= -1;
k--;
}
int sum = 0;
for(int i = 0; i < nums.size(); i++)
{
sum += nums[i];
}
return sum;
}
};
leetcode 134 加油站
暴力解法
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
for (int i = 0; i < cost.size(); i++) {
int rest = gas[i] - cost[i]; // 记录剩余油量
int next = (i + 1) % cost.size();
while (rest > 0 && next != i) { // 模拟以i为起点行驶一圈
rest += gas[next] - cost[next];
next = (i + 1) % cost.size();
}
if (rest > 0 && next == i) return i; //如果以i为起点跑一圈,剩余油量>=0,返回该起始位置
}
return -1;
}
};
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int curSum = 0;
int totalSum = 0;
int start = 0;
for(int i = 0; i < gas.size(); i++)
{
curSum += gas[i] - cost[i];
totalSum += gas[i] - cost[i];
if(curSum < 0)
{
start = i + 1;
curSum = 0;
}
}
if(totalSum < 0) return -1;
return start;
}
};
//情况一:如果gas的总和小于cost总和,那么无论从哪里出发,一定是跑不了一圈的
//情况二:rest[i] = gas[i]-cost[i]为一天剩下的油,i从0开始计算累加到最后一站,如果累加没有出现负数,说明从0出发,油就没有断过,那么0就是起点。
//情况三:如果累加的最小值是负数,汽车就要从非0节点出发,从后向前,看哪个节点能这个负数填平,能把这个负数填平的节点就是出发节点。
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int minDiff = INT_MAX;
int totalDiff = 0;
for(int i = 0; i < gas.size(); i++)
{
int rest = gas[i] - cost[i];
totalDiff += rest;
if(totalDiff < minDiff)
{
minDiff = totalDiff;
}
}
if(totalDiff < 0) return -1;
if(minDiff >= 0) return 0;
for(int i = gas.size()-1; i >=0; i--)
{
int rest = gas[i] - cost[i];
minDiff += rest;
if(minDiff >= 0)
{
return i;
}
}
return -1;
}
};
leetcode 135 分发糖果
class Solution {
public:
int candy(vector<int>& ratings) {
vector<int> candyVec(ratings.size(), 1);
for(int i = 1; i < ratings.size(); i++){
if(ratings[i] > ratings[i-1])
candyVec[i] = candyVec[i-1] + 1;
}
for(int i = ratings.size()-2; i >= 0; i--){
if(ratings[i] > ratings[i+1])
candyVec[i] = max(candyVec[i], candyVec[i+1]+1);
}
int result = 0;
for(auto ele : candyVec) result += ele;
return result;
}
};
leetcode 860 柠檬水找零
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int hashVec[21] = {0};
for(int i = 0; i < bills.size(); i++) {
if(bills[i] == 5) {
hashVec[5]++;
}
else if(bills[i] == 10) {
hashVec[10]++;
if(hashVec[5] > 0) {
hashVec[5]--;
}
else {
return false;
}
}
else {
hashVec[20]++;
bool flag = false;
if(hashVec[10] > 0) {
hashVec[10]--;
flag = true;
}
if(hashVec[5] > 0 && flag == true) {
hashVec[5]--;
}
else {
if(hashVec[5] == 0)
return false;
}
if(hashVec[5] > 0 && flag == false) {
hashVec[5] = hashVec[5] - 3;
if(hashVec[5] < 0) {
return false;
}
}
}
}
return true;
}
};
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int five = 0, ten = 0, twenty = 0;
for(auto bill : bills) {
if(bill == 5) {
five++;
}
else if(bill == 10) {
if(five <= 0) {
return false;
}
ten++;
five--;
}
else {
if(ten > 0 && five > 0) {
ten--;
five--;
}
else if (five >= 3) {
five -= 3;
}
else {
return false;
}
}
}
return true;
}
};
leetcode 406 根据身高重建队列
class Solution {
static bool cmp(const vector<int>& a, const vector<int>& b) {
if(a[0] == b[0]) return a[1] < b[1];
else return a[0] > b[0];
}
public:
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
sort(people.begin(), people.end(), cmp);
vector<vector<int>> queue;
for(int i = 0; i < people.size(); i++) {
int pos = people[i][1];
queue.insert(queue.begin()+pos, people[i]);
}
return queue;
}
};
class Solution {
static bool cmp(const vector<int>& a, const vector<int>& b) {
if(a[0] == b[0]) return a[1] < b[1];
else return a[0] > b[0];
}
public:
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
sort(people.begin(), people.end(), cmp);
list<vector<int>> queue;
for(int i = 0; i < people.size(); i++) {
int pos = people[i][1];
auto it = queue.begin();
while(pos) {
it++;
pos--;
}
queue.insert(it, people[i]);
}
return vector<vector<int>>(queue.begin(), queue.end());
}
};
leetcode 452 用最少数量的箭引爆气球
class Solution {
static bool cmp(const vector<int>& a, const vector<int>& b) {
return a[0] < b[0];
}
public:
int findMinArrowShots(vector<vector<int>>& points) {
sort(points.begin(), points.end(), cmp);
int start = points[0][0];
int end = points[0][1];
int shootNum = 1;
for(int i = 1; i < points.size(); i++) {
if(points[i][0] <= end) {
start = points[i][0];
if(points[i][1] <= end) {
end = points[i][1];
}
}
else {
shootNum++;
start = points[i][0];
end = points[i][1];
}
}
return shootNum;
}
};
leetcode 435 无重叠区间
class Solution {
static bool cmp(const vector<int>& a, const vector<int>& b) {
return a[1] < b[1];
}
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end(), cmp);
int rightBound = intervals[0][1];
int overlap = 0;
for(int i = 1; i < intervals.size(); i++) {
if(intervals[i][0] < rightBound) {
overlap++;
continue;
}
else {
rightBound = intervals[i][1];
}
}
return overlap;
}
};
leetcode 763 划分字母区间
class Solution {
public:
vector<int> partitionLabels(string s) {
int hashVec[27] = {0};
for(int i = 0; i < s.size(); i++) {
hashVec[s[i]-'a'] = i; //统计每个字母最后出现的下标
}
int right = 0;
int left = 0;
vector<int> result;
for(int i = 0; i < s.size(); i++) {
right = max(right, hashVec[s[i]-'a']); //不断更新该片段中字母最后出现的下标
if(right == i) { //片段分割点
result.push_back(right - left + 1);
left = i + 1;
}
}
return result;
}
};
leetcode 56 合并区间
按照左边界排序,排序之后局部最优:每次合并都取最大的右边界,这样就可以合并更多的区间了,整体最优:合并所有重叠的区间。
class Solution {
static bool cmp(const vector<int>& a, const vector<int>& b) { //按左边界进行从小到大排序
if(a[0] == b[0]) return a[1] < b[1];
return a[0] < b[0];
}
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end(), cmp);
int left = intervals[0][0];
int right = intervals[0][1];
vector<vector<int>> result;
for(int i = 1; i < intervals.size(); i++) {
if(intervals[i][0] <= right) { //如果当前区间的left小于前面区间的right,说明区间重叠可以合并
if(intervals[i][1] > right) //如果当前区间的right大于前面区间的right,更新一下right最大值,扩大可以合并区间范围
right = intervals[i][1];
}
else {
result.push_back(vector<int>{left, right}); //说明区间不重叠,更新新的left和right值
left = intervals[i][0];
right = intervals[i][1];
}
}
result.push_back(vector<int>{left, right});
return result;
}
};
leetcode 738 单调递增的数字
遍历过程中,要用之前处理的结果推导以后的内容,首先想到从后往前遍历
//向前遍历,前一项大于后一项,前一项就减 1,后面所有都变成 9
class Solution {
public:
int monotoneIncreasingDigits(int n) {
string str = to_string(n);
int flag = str.size();
for(int i = str.size()-1; i >= 1; i--) {
if(str[i-1] > str[i]) {
str[i-1]--;
flag = i;
}
}
for(int i = flag; i < str.size(); i++) {
str[i] = '9';
}
return stoi(str);
}
};
leetcode 968 监控二叉树
//0:该节点无覆盖
//1:本节点有摄像头
//2:本节点有覆盖
class Solution {
public:
int minCameraCover(TreeNode* root) {
int result = 0;
if(recursiveFunc(root, result) == 0) { //最后处理头节点状态
result++;
}
return result;
}
int recursiveFunc(TreeNode* node, int& result) {
if(node == NULL) {
return 2;
}
int left = recursiveFunc(node->left, result);
int right = recursiveFunc(node->right, result);
if(left == 2 && right == 2) {
return 0;
}
if(left == 0 || right == 0) {
result++;
return 1;
}
if(left == 1 || right == 1) {
return 2;
}
return -1;
}
};