Leetcode刷题_贪心相关_c++版

(1)455分发饼干–简单

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
在这里插入图片描述

class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        //升序排序
        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;
    }
};

在这里插入图片描述

(2)376摆动序列–中等

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。

例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。

相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。

给你一个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的长度 。
在这里插入图片描述
在这里插入图片描述
连续递增或递减时,选择红色点

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        if(nums.size()==0) return 0;
        if(nums.size()==1) return 1;
        //利用状态机转换
        //三个状态
        static const int begin = 0;
        static const int up = 1;
        static const int down = 2;
        int STATE = begin;
        int maxLength = 1;//初始长度为1
        for(int i = 1; i< nums.size(); i++){
            switch(STATE){
            //当状态为begin时
                case begin:
                    if(nums[i]>nums[i-1]){
                        STATE = up;
                        maxLength++;
                    }
                    else if(nums[i] < nums[i-1]){
                        STATE = down;
                        maxLength++;
                    }
                    //如果两数相等,不发生状态转换
                    break;
                case up:
                    
                    if(nums[i] < nums[i-1]){
                        STATE = down;
                        maxLength ++;
                    }
                    //如果两数相等或后一个大于前一个,不发生状态转换
                    break;
                case down:
                    
                    if(nums[i] > nums[i-1]){
                        STATE = up;
                        maxLength ++;
                    }
                    //如果两数相等或后一个小于前一个,不发生状态转换
                    break;
            }
        }return maxLength;



    }
};

在这里插入图片描述

(3)402移调k位数–中等

给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。
在这里插入图片描述

#include<string>
using namespace std;
class Solution {
public:
    string removeKdigits(string num, int k) {
        if(k >= num.size()) return "0";
        vector<int> S;
        string result = "";
        int number;
        for(int i = 0; i< num.length(); i++){
            //从高位开始,保留尽可能小的数
            number = num[i] - '0';//字符串转换为数字,0要用单引号,表示一个字符,双引号表示一个字符串
            while(S.size()> 0 && number < S[S.size() - 1] && k>0){
                //if(S.size() == 1 && number == 0) break;//如果栈中只有一个元素,且新来的元素是0,不能用0替换栈顶
                S.pop_back();//如果新元素比栈顶小,就保留新元素
                k--;
            }
                
            //0不能作为开头元素
            if(number != 0 || S.size() != 0 ){
                S.push_back(number);
            }
            
        }
        if(S.size() == 0 || k >= S.size()) return "0";

        //如果序列都遍历完而k还未用完时,得到的栈中元素一定是单调递增的。
        while(k > 0) {
            S.pop_back();
            k--;
        }

        if(S.size() == 0) return "0";

        for(int i = 0; i< S.size(); i++){
            //char tmp = S[i]+'0';
            //result = result+ tmp;//这样转字符,超长用例会报超时,不知道为啥
            result.append(1,S[i]+'0');//在当前字符串结尾添加1个字符
		
        }
        return result;
    }
};

在这里插入图片描述
append函数的用法参考:https://blog.csdn.net/weixin_42258743/article/details/107964192

(4)55跳跃游戏

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。
在这里插入图片描述

法一:

class Solution {
public:
    bool canJump(vector<int>& nums) {
        //关键在于如何选择下一条的节点
        //下一跳能跳的越远越有利,因此选择能达到最远的节点
        if(nums.size()== 1) return true;
        int next_index = -1;
        int index = 0;
        int max_distance = 0;//此处赋值为负数的话会导致与size函数返回值(无符号数)比较出错
        while((index+max_distance <= (nums.size()-1)) && (next_index != index)){
            //下一跳等于当前下标,则证明不能到达
            //下一条等于大于总长度,则证明可到达
            max_distance = 0;
            //index更新至下一跳处
            if (next_index != -1) index = next_index;
            //cout<<"index = "<< index << " next_index = "<<next_index<<endl;
            if(nums[index] == 0) break;
            else{
                for(int i = 1; i<= nums[index] && (i+index<nums.size()); i++){
                //遍历从index到index+nums[i]能到达的所有元素,找出最远距离
                    if((i+nums[i+index]) > max_distance){
                        max_distance = (i+nums[i+index]);
                        next_index = index+i;
                        //cout<< " i="<<i<<" max_distance=" << max_distance<<endl;
                    }

                }
            }

            //cout<<"index = "<< index << " next_index = "<<next_index<<endl;
            
        }
        if((index+max_distance >= (nums.size()-1)) ) return true;
        return false;
    }
};

在这里插入图片描述

法二:

class Solution {
public:
    bool canJump(vector<int>& nums) {
        if(nums.size() == 1) return true;
        int length = nums.size();//总长度
        int tmp = nums[0];//tmp记录当前能到达的最远距离
        int i = 1;
        for(i; (i< length && tmp > 0); i++){
            tmp--;
            cout<< "tmp="<<tmp<<" nums[" << i<<"]=" << nums[i]<< " ";
            //如果当前节点能到达的距离比之前的远,tmp更新为当前节点的值
            if(nums[i] > tmp){ 
                tmp = nums[i];
            }
            //cout<< tmp << "picked"<<endl;
        }
        //cout<< "tmp="<<tmp<<endl;
        if(i >= length && tmp >= 0) return true;
        return false;

    }
};

在这里插入图片描述

(5)45跳跃游戏二–中等

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

0 <= j <= nums[i]
i + j < n
返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。

在这里插入图片描述
此题根据跳跃游戏的 法一 改编得到
https://blog.csdn.net/weixin_44343355/article/details/128772719

class Solution {
public:
    int jump(vector<int>& nums) {
        //关键在于如何选择下一条的节点
        //下一跳能跳的越远越有利,因此选择能达到最远的节点
        if(nums.size() < 2) return 0;
        int next_index = 0;
        int index = 0;
        int max_distance = nums[0];//此处赋值为负数的话会导致与size函数返回值(无符号数)比较出错
        int jump_times = 0;
        int length = nums.size();
        while(((max_distance) < length -1)){
            //最远能到的距离等于大于总长度,则证明可到达

            for(int i = 1; i<= nums[index] && (i+index<nums.size()); i++){
            //遍历从index到index+nums[i]能到达的所有元素,找出最远距离
                if((i+nums[i+index]+index) > max_distance){
                    max_distance = (i+nums[i+index]+index);
                    next_index = index+i;
                    
                    //cout<< " i="<<i<<" max_distance=" << max_distance<<endl;
                }
                
            }
            index = next_index;
            jump_times++;

        }
        return jump_times+1;


    }
};

在这里插入图片描述

(6)452用最少数量的箭引爆气球–中等

有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。

一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。

给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。

在这里插入图片描述

//注意,此处需要加引用
bool cmp(const vector<int> &a, const vector<int> &b){
    return a[0] < b[0];
}

class Solution {
public:

    int findMinArrowShots(vector<vector<int>>& points) {
        if(points.size() == 1) return 1;
        sort(points.begin(), points.end(), cmp);//按左端点从小到大升序排序
        int length = points.size();
        int shootNum = 1;
        int shooter_begin = points[0][0];
        int shooter_end = points[0][1];
        for(int i = 1;i<length;i++){
            //如果新节点的左端点在shooter的范围内,则说明两区间有重合部分,需要一箭即可射穿
            if(points[i][0] >= shooter_begin && points[i][0] <= shooter_end){
                //更新shooter的左端点
                shooter_begin = points[i][0];
                
                //如果shooter的右端点大于新节点的右端点,则更新右端点
                if(points[i][1] <= shooter_end){
                    shooter_end = points[i][1];
                }
            }
            //如果无交集,则新选择一个射手
            else{
                shootNum++;
                shooter_begin = points[i][0];
                shooter_end = points[i][1];
            }
        }
        return shootNum;

    }
};

在这里插入图片描述

(7)871最低加油次数–困难

汽车从起点出发驶向目的地,该目的地位于出发位置东面 target 英里处。

沿途有加油站,用数组 stations 表示。其中 stations[i] = [positioni, fueli] 表示第 i 个加油站位于出发位置东面 positioni 英里处,并且有 fueli 升汽油。

假设汽车油箱的容量是无限的,其中最初有 startFuel 升燃料。它每行驶 1 英里就会用掉 1 升汽油。当汽车到达加油站时,它可能停下来加油,将所有汽油从加油站转移到汽车中。

为了到达目的地,汽车所必要的最低加油次数是多少?如果无法到达目的地,则返回 -1 。

注意:如果汽车到达加油站时剩余燃料为 0,它仍然可以在那里加油。如果汽车到达目的地时剩余燃料为 0,仍然认为它已经到达目的地。

在这里插入图片描述
此题的思路与跳跃游戏二中的思路类似,都是求最小跳跃次数,用贪心的思想

class Solution {
public:
    int minRefuelStops(int target, int startFuel, vector<vector<int>>& stations) {
        if(stations.size() == 0){
            if(target > startFuel) return -1;
            else return 0;
        }
        // if(stations.size() == 1){
        //     if(target < startFuel) return 0;
        //     else if(stations[0][0] > startFuel || (stations[0][1]+startFuel) < target) return -1;
        //     else return 1;
        // }
        int leftFuel = startFuel;//剩余油量
        priority_queue<int, vector<int>, less<int>> maxFuel;//途中站点的最大油量,加过油的站点就不能再加油了,所以用 堆来存储,依次输出最大站点
        int addFuelTimes = 0;//加油次数
        int i = 0;
        int maxfuel = 0;
        vector<int> target_vector;
        target_vector.push_back(target);
        target_vector.push_back(0);
        stations.push_back(target_vector);
        for(i = 0; i< stations.size(); i++){
            //cout<< "i="<<i<<" ";
            leftFuel = startFuel - stations[i][0];//剩余油量
            //剩余油量小于等于0,则表明过程中需要加一次油
            //无法到达该点,先加油
            while(leftFuel < 0 && maxFuel.size()>0){
                
                maxfuel = maxFuel.top();
                maxFuel.pop();
                leftFuel = leftFuel + maxfuel;
                startFuel = startFuel +maxfuel;
                addFuelTimes ++;
            }
            if(leftFuel<0) return -1;
            maxFuel.push(stations[i][1]);

            //前面所有站点的油都加完了,如果还到不了该点,返回无法到达
            
            //cout<<endl;
            

            }return addFuelTimes;
            
    
        }
    
};  

在这里插入图片描述

(8)51N皇后–困难

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
在这里插入图片描述

class Solution {
public:
	//放置皇后
    void putDownMark(int x, int y, vector<vector<int>> &mark){
        //方向数组,8个方向
        static const int dx[]={1,1,0,-1,-1,-1,0,1};
        static const int dy[]={0,1,1,1,0,-1,-1,-1};
        mark[x][y] = 1;
        //8个方向所有位置都变成1
        for(int i = 0; i<mark.size(); i++){
            for(int j  = 0; j<8; j++){
                int new_x = x+i*dx[j];
                int new_y = y+i*dy[j];
                if(new_x<mark.size() && new_x >= 0 && new_y<mark.size()&& new_y>=0){
                    mark[new_x][new_y] = 1;
                }
            }
        }


    }

    void generate(vector<vector<int>> mark, vector<string> location,vector<vector<string>> &result, int i){
        //放到最后一行了,说明这个放法可以放置成功
        if(i == mark.size()){
            result.push_back(location);
            return;
        }
        //每一行尝试所有位置
        for(int j=0; j<mark.size();j++){
            if(mark[i][j] == 0){
                vector<vector<int>> tmp_mark = mark;
                putDownMark(i,j,mark);
                location[i][j] = 'Q';
                generate(mark,  location, result, i+1);
                //if(statute == 1) return;
                //回溯,方便继续判断下一个位置
                mark = tmp_mark;
                location[i][j] = '.';
                
            }
        }
    }
    
    vector<vector<string>> solveNQueens(int n) {
        //皇后规则,在某点放置了皇后,则它可以吃前后左右、左上、右上、左下、右下任意多步的棋子
        //n*n的棋盘放n个皇后,则每行都得放一个
        vector<string> location;
        vector<vector<string>> result;
        vector<vector<int>> mark;
        vector<int> tmp;
        //vector<string> tmp1;
        //int statute = 0;
        //初始化mark数组
        for(int i = 0;i <n;i++){
            mark.push_back(tmp);
            //location.push_back(tmp1);
            //location[i].push_back("");
            for(int j = 0; j<n ;j++)
                mark[i].push_back(0);
            location.push_back("");
            location[i].append(n,'.') ;
            //cout<<i;
        }

        generate(mark, location,result, 0);
        return result;


    }
};

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值