代码随想录——贪心算法

贪心无套路,也没有框架之类的,需要多看多练培养感觉才能想到贪心的思路。

题目:455.分发饼干

class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
    int i=0;
    int j=0; 
    int start=0;
    //对饼干的尺寸以及小孩的胃口进行排序
    sort(g.begin(),g.end());
    sort(s.begin(),s.end());
    //饼干尺寸从小到大遍历,去满足小孩的胃口
    for(i=0;i<g.size();i++)
    {
        for(j=start;j<s.size();j++)
        {
           if(s[j]>=g[i])
           {
               start=j+1;
               break;
           }  
        }
        if(j==s.size())
        break;
    }
    return i;
    }
};

题目:376.摆动序列

//本题一共分三种情况:
//情况1:原数组中元素个数<3,且存在[1,1]类似情况,此时正确返回结果应为1,但是采用一般方法会返回2;
//情况2:原数组中存在[1,1,1]情况,三数相等,此时正确返回结果应为1,但是采用一般方法会返回2;
//情况3:一般情况,将array[array.size()-1]、nums[i]、nums[i+1]三数进行比较,只要不连续递增/递减,则将nums[i]压入array。
//注意不要在原数组nums上使用nums[i-1]、nums[i]、nums[i+1]三数进行比较,因为如果在遍历的时候三数一起遍历会导致例如[1,3,3,2]数组,会认为[1,3,3]/[3,3,2]均不满足,而获取不到[1,3,2],最后返回错误结果为2,而不是3。
class Solution {
public:
    vector<int> array;
    int wiggleMaxLength(vector<int>& nums) {
    //情况一
    if(nums.size()<3)
    {
        if(nums.size()==2&&nums[0]==nums[1])
        return 1;
        else 
        return nums.size(); 
    }
    //情况二 
    if((nums.size()==3)&&(nums[0]==nums[1])&&(nums[1]==nums[2]))
    return 1;
    //情况三
    array.push_back(nums[0]);
    for(int i=1;i<nums.size()-1;i++)
    {
        if((nums[i]-array[array.size()-1])*(nums[i]-nums[i+1])>0)
        {
            array.push_back(nums[i]);
        }
    }
    //除nums[0]外,数组其余元素只计算凸起值和下凹值,没有计算数组最后元素,此处应加上
    return array.size()+1;
    }
};

 题目:53.最大子数组和

//如果连续数组元素[h,i]之和为负数,则下次计算时应直接跳过该连续数组,直接从sum+=nums[i+1]开始
//不必考虑说[h,h+k]之和为负数,[h+k+1,i]为整数,需要弹出[h,h+k]保留[h+k+1,i]的情况
//因为在一开始[h,h+k]之和为负数时,该串数组就已经会被舍弃。
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
    int result=nums[0];
    int sum=0;
    for(int i=0;i<nums.size();i++)
    {
        //累计连续数组元素之和
       sum+=nums[i];
       //更新最大值【注意:这三步操作顺序不能乱】
       if(sum>result)
       result=sum;
       //当连续数组元素之和<0时,归零
       if(sum<0)
       sum=0;
    }
    return result;
    }
};

题目:122.买卖股票的最佳时机||

//只要今天价格闭昨天价格高,则将股票在今天卖出去,并"假装"买下今天的股票;
//今天股票价格低于昨天,则更新"假装"购买股票的价格
//此处的"假装",即没有真正扣钱,只有当卖股票操作生效时才有用。
class Solution {
public:
    int maxProfit(vector<int>& prices) {
    int temp=prices[0];
    int sum=0; 
    for(int i=0;i<prices.size();i++)
    {
        //假装购买今天的股票
        if(temp>prices[i])
        temp=prices[i];
        //卖出股票,并假装购买今天的股票
        else
        {
            sum+=(prices[i]-temp);
            temp=prices[i];
        }
    }
    return sum;
    }
};

题目:55.跳跃游戏

//贪心原则:判断当前节点可以跳跃的范围内,可以到最远的点,用其更新当前节点
//在更新后,判断该点是否可以跳到最后一个节点,如果可以,则返回true;
//在更新后,如果当前节点无法跳跃到最后一个节点,且当前节点可跳跃值为0,则返回false;
class Solution {
public:
    bool canJump(vector<int>& nums) {
    int tempMax=nums[0];

    for(int i=0;i<nums.size();i++)
    {
       int Max=0;
       int J;
       //从当前节点i开始遍历
       for(int j=i;j<=i+tempMax;j++)
       {
           //判断当前节点tempMax与下一节点nums[j]谁能走的更远
           //【注意此处判别条件用<=号,否则[2,1,0]无法将当前节点更新到0,导致死循环】
           if(Max<=(nums[j]+(j-i)))
           {
               Max=nums[j]+(j-i);//更新更远值
               tempMax=nums[j];//更新当前节点
               J=j;//记住当前节点在原数组中的位置

               //判断当前节点是否可以到达最后一节点
               if(nums[j]+j>=nums.size()-1)
               return true;
           }
       }
       //更新当前节点下标
       i=J-1;
       if(tempMax==0)
       return false;
    }
    return true;
    }
};

题目:45.跳跃游戏||

class Solution {
public:
    int jump(vector<int>& nums) {
    //当数组元素个数为1时,不用跳跃即可到达最后一个节点
    int step=0;
    if(nums.size()==1)
    return 0;
    //在当前节点可以跳跃的区间内寻找可以跳跃最远的那个节点
    for(int i=1;i<nums.size();i++)
    {
        int start=0;//用于比较两个节点谁跳得远,弥补索引差
        int tempi=0;//保存当前区间内跳的最远的那个节点的索引
        int range=nums[i-1];//保存当前节点的跳跃区间
        step++;//记录跳跃次数
        
        //如果上一节点的索引+跳跃区间>=数组大小,说明可以跳跃到最后一个节点,则退出
        if(range+i-1>=nums.size()-1)
        return step;       
        //遍历寻找上一节点跳跃区间内能够跳跃最远的节点
        for(int j=i;j<i+nums[i-1]&&j<nums.size();j++)
        {
            start++;
            if(nums[j]+start>=range)
            {
                //更新当前节点跳跃区间,保存索引
                range=nums[j]+start;
                tempi=j;
            }
        }
        //更新当前节点的索引
        i=tempi;
    }
    return step;
    }
};

题目:1005.K次取反后最大化的数组和

class Solution {
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
    int sum=0;
    //每次取反都将数组进行排序,对最小的数nums[0]进行取反
    for(int i=0;i<k;i++)
    {
        sort(nums.begin(),nums.end());
        nums[0]=-nums[0];
    }
    //计算数组元素总和
    for(int j=0;j<nums.size();j++)
    sum+=nums[j];
    return sum;
    }
};

题目:134.加油站

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
    int sum=0;//记录当前油箱油量
    int sum1=0;//记录gas油量之和-cost油量之和,当sum1>0说明一定存在某一始发站满足题目要求
    int start=0;
    for(int i=0;i<gas.size();i++)
    {
        sum1+=(gas[i]-cost[i]);
        //记录始发站start到当前站i路程内,油箱内的当前油量
        sum+=(gas[i]-cost[i]);
        //如果油箱内的油量<0,此时可以确定以下两点:
        //1.原始发站start肯定不能是合法始发站;
        //2.[start,i]中不存在合法始发站,因为如果存在[j+1,i]内油箱油量累计>0
        //且[start,j]没被舍弃说明[start,j]内油箱油量累计>0。则[start,i]内油箱油量累计>0,不合理!!!!!
        if(sum<0)
        {
            //因此归零当前油箱油量,重置始发站为i+1
            sum=0;
            start=i+1;
        }
    }
    //当sum1<0说明,环形路径内,加油总量小于耗油量,一定无法环形一周
    if(sum1<0)
    return -1;
    return start;

    }
};

题目:135.分发糖果

//注意:本题的难点在于,无法一次同时比较当前孩子的左右两边孩子的分数进行分配糖果
//所以只能先只比较当前孩子与其左孩子相比,分数越高糖果数要大
//从左到右分配完毕以后,开始从右到左比较
//比较当前孩子与其右孩子相比,分数越高糖果数要大
class Solution {
public:
    int candy(vector<int>& ratings) {
    if(ratings.size()==1)
    return 1;
    //先将所有孩子的糖果数都初始化为1  
    vector<int> sugur(ratings.size(),1);
    //从左向右比较,当右边孩子的分数闭左边高,右边孩子的糖果数=左边孩子糖果数+1
    for(int i=1;i<ratings.size();i++)
    {
        if(ratings[i]>ratings[i-1])
        {
            sugur[i]=sugur[i-1]+1;
        }
    }
    //从左向右比较,如果左孩子的分数比右边高,如果左孩子的糖果数本来就比右孩子高,则不变,
    //否则左孩子的糖果数=右边孩子糖果数+1
    for(int i=ratings.size()-2;i>=0;i--)
    {
        if(ratings[i]>ratings[i+1])
        {
            sugur[i]=max(sugur[i],sugur[i+1]+1);
        }
    }
    //记录分发给孩子的糖果总数
    int SUM=0;
    for(int i=0;i<ratings.size();i++)
    {
        SUM+=sugur[i];
    }
 return SUM;
    }
};

题目:406.根据身高重建队列

// 排序完的people: [[7,0], [7,1], [6,1], [5,0], [5,2],[4,4]]
// 插入的过程:排序完后的数组,每次插入result数组的位置为people[i][1];
//     插入[7,0]:[[7,0]]
//     插入[7,1]:[[7,0],[7,1]]
//     插入[6,1]:[[7,0],[6,1],[7,1]]
//     插入[5,0]:[[5,0],[7,0],[6,1],[7,1]]
//     插入[5,2]:[[5,0],[7,0],[5,2],[6,1],[7,1]]
//     插入[4,4]:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]		
// 	数组的插入操作太耗时间,可以考虑换成链表操作
class Solution {
public:
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
    //1.将数组进行排序 [[7,1], [7,0], [6,1], [5,2],[5,0], [4,4]]
    sort(people.begin(),people.end());
    reverse(people.begin(),people.end());
    //记录每种身高的人数
    unordered_map<int,int> mat;
    for(int i=0;i<people.size();i++)
    {
        mat[people[i][0]]++;
    }
    //2.将数组进行排序排序成[[7,0], [7,1], [6,1], [5,0], [5,2],[4,4]]
    for(int j=0;j<people.size();j++)
    {
        int len=mat[people[j][0]];
        reverse(people.begin()+j,people.begin()+j+len);
        j=j+len-1;
    }
    //3.将数组中的元素挨个插入结果数组中
    vector<vector<int>> result;
    for(int i=0;i<people.size();i++)
    {//注意:数组元素的插入方法
        result.insert(result.begin()+people[i][1],people[i]);
    }
    return result;
    }
};

题目:452.用最少数量的箭引爆气球

class Solution {
public:
    int findMinArrowShots(vector<vector<int>>& points) {
    int num=1;
    //首先需要将气球引爆区间进行排序
    sort(points.begin(),points.end());
    //创建一个二维数组用来临时保存当前弓箭可以射穿的区间
    vector<int> array=points[0];
    for(int i=1;i<points.size();i++)
    {
        //如果当前遍历的区间与上一弓箭可以射穿的区间存在区间重合,取其交集,并继续遍历下一气球引爆区间
       if(array[1]>=points[i][0])
       {
           array[0]=points[i][0];
           array[1]=min(array[1],points[i][1]);
           continue;
       }
       //如果当前遍历的区间与上一弓箭可以射穿的区间不存在交集,则累加弓箭数,更新当前弓箭可以射穿的区间
       else
       {
           num++;
           array=points[i];
       }
    }
    return num;
    }
};

题目:763.划分字母区间

class Solution {
public:
    //用于判断两个区间之间是否有重合
    bool isInpart(vector<int> array1,vector<int> array2)
    {
        if(array2[0]>=array1[0]&&array2[0]<array1[1])
        return true;
        return false;
    } 
    
    vector<int> partitionLabels(string s) {
    unordered_map<char,int> mat;
    vector<vector<int>> array;
    //采用哈希表获取字符串中每个字符的左区间
    for(int i=0;i<s.length();i++)
    {
        if(mat[s[i]]==0)
        mat[s[i]]=i+1;
        else 
        continue;
    }
    //采用哈希表获取字符串中每个字符的右区间
    for(int j=s.length()-1;j>=0;j--)
    {
        if(mat[s[j]]!=0)
        {
            array.push_back({mat[s[j]],j+1});
            mat[s[j]]=0;
        }  
        else 
        continue;
    }
    //将获取的区间集合数组进行排序!!!
    sort(array.begin(),array.end());
   
    vector<vector<int>> temp;
    temp.push_back(array[0]);
    //将所有存在交集的区间进行合并
    for(int i=1;i<array.size();i++)
    {
       if(isInpart(temp[temp.size()-1],array[i]))
       {
           temp[temp.size()-1][1]=max(array[i][1],temp[temp.size()-1][1]);
       }
       else
       {
           temp.push_back(array[i]);
       }
    }
    vector<int> result;
    //计算所有合并后的区间长度,并+1
    for(int i=0;i<temp.size();i++)
    {
        result.push_back(temp[i][1]-temp[i][0]+1);
    }
    return result;
    }
};

题目:56.合并区间

class Solution {
public:
    //用于判断两个区间之间是否有重合
    bool isInpart(vector<int> array1,vector<int> array2)
    {
        if(array2[0]>=array1[0]&&array2[0]<=array1[1])
        return true;
        return false;
    }

    vector<vector<int>> merge(vector<vector<int>>& intervals) {
    vector<vector<int>> temp;
    //对数组中的区间进行排序
    sort(intervals.begin(),intervals.end());
    temp.push_back(intervals[0]);
    //将所有存在交集的区间进行合并
    for(int i=1;i<intervals.size();i++)
    {
       if(isInpart(temp[temp.size()-1],intervals[i]))
       {
           temp[temp.size()-1][1]=max(intervals[i][1],temp[temp.size()-1][1]);
       }
       else
       {
           temp.push_back(intervals[i]);
       }
    }
    return temp;
    }
};

题目:738.单调递增的数字

class Solution {
public:
    long monotoneIncreasingDigits(int n) {
    //情况一:个位数直接返回
    if(n<10)
    return n;
    //情况二:普通情况
    //先将n拆解成各个数存入数组,用于后续判断
    vector<int> array;
    while(n>0)
    {
       int temp=n%10;
       array.push_back(temp);
       n=n/10;
    }
    //eg:(1)13234256--->直接将4减1,将2及之后的数都改为9----->13233999
    //    (2)13233999----->将3减1,直接将2及以后的数都改为9------->12999999
    for(int i=0;i<array.size()-1;i++)
    {
        if(array[i]<array[i+1])
        {
            array[i+1]=array[i+1]-1;
            int n=i;
            while(n>=0)
            {
                array[n]=9;
                n--;
            }
        }
    }
    //最后再将数组中的数组合成一个整数返回
    long result=0;
    for(int i=array.size()-1;i>=0;i--)
    {
       result+=array[i];
       result=result*10;
    }
    return result/10;
    }
};

题目:714.买卖股票的最佳时机含手续费

//用动态规划做!!!!!!
class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
    int buy=prices[0];
    int money=0;
    for(int i=1;i<prices.size();i++)
    {
        if(prices[i]-buy-fee>0)
        {
            money+=prices[i]-buy-fee;
            buy=prices[i]-fee;//很关键,需要减去fee,不理解
        }
        if(prices[i]<buy)
        buy=prices[i];
    }
    return money;
    }
};

可以从程序中看出,在卖出股票当天,如果购买股票,购买的价格并不是当天股票的价格,而是股票的价格-手续费。原因如下:

卖出股票当天其实并没有真正将股票卖出,只是暂时收获利润。此时可以看成当天买股票时的价格=当初买股票的价格+利润+手续费。(利润+手续费)这部分钱一部分收入囊中,一部分交手续。即可以看成之前从没有做过股票交易,但是交了手续肥。所以当天买股票的价格=当天股票的价格-手续费。其实我也不大清楚,这种题最好用动态规划做.........

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值