LeetCode贪心算法

316. 去除重复字母

给定一个仅包含小写字母的字符串,去除字符串中重复的字母,使得每个字母只出现一次。需保证返回结果的字典序最小(要求不能打乱其他字符的相对位置)。

链接:https://leetcode-cn.com/problems/remove-duplicate-letters/

/*贪心思想:首先统计所有字符出现的次数,以及字符是否写入,用栈辅助,对于当前字符,如果栈顶元素字典序大并且该元素次数大于0 说明后面还有,就删除该栈顶元素,继续比较栈中的下一个元素。处理完当前字符,当前字符的出现次数减1;
*/
class Solution {
public:
    string removeDuplicateLetters(string s) {
     
        stack<char> r;
        string res;
        int i;
        int count[26] = {0};  //所有字符出现的次数
        int write[26] = {0};  //字符是否写入
        if(s.size()==0)
            return "";
        for(i=0;i<s.size();i++)
        {
            count[s[i]-'a']++;
        }
        
        r.push(s[0]);
      
        count[s[0]-'a']--;
        write[s[0]-'a'] ++;
        
        for(i=1;i<s.size();i++)
        {
            //r.push(s[i]);
            //cout<<r.top();
            
            while(r.size()!=0 && write[s[i]-'a'] == 0 && r.top() > s[i] && count[r.top()-'a'] >0)
            {
              
                write[r.top()-'a'] --;
                r.pop();

            }  //比较栈中的元素的字典序 如果栈顶大,并且目前的字符没有写入 栈顶元素后面还有,则去除栈顶元素。 
            
            if(write[s[i]-'a'] == 0){
              r.push(s[i]);
              write[s[i] - 'a'] ++;
            }
            count[s[i]-'a']--;
              
        }
       
        while(!r.empty())
        {
          //  cout<<"res"<<r.top();
            res+=r.top();
            r.pop();
        } 
        
        reverse(res.begin(),res.end());
        return res;
    }
};

 

135. 分发糖果

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。

你需要按照以下要求,帮助老师给这些孩子分发糖果:

每个孩子至少分配到 1 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?


链接:https://leetcode-cn.com/problems/candy

 

/*贪心思想:先把每个糖果值都赋予1
  两次遍历,第一次从左到右,比较i和i左边的i-1大小,i大就在i-1的基础上加1
           第二次从右到左,比较i和i右边的i+1大小,i大并且糖果少就在i+1的基础上加1
*/
class Solution {
public:
    int candy(vector<int>& ratings) {
     
        int i=0;
        int result = 0;
        int c[ratings.size()];
        for(i=0;i<ratings.size();i++)
            c[i] = 1;
        for(i=1;i<ratings.size();i++)
        {
            if(ratings[i]>ratings[i-1])
            {
               
                c[i] = c[i-1]+1 ;
               
            }
        }
        for(i=ratings.size()-2;i>=0;i--)
        {
            if(ratings[i]>ratings[i+1] && c[i]<=c[i+1])
                c[i] = c[i+1]+1;
        }
        for(i=0;i<ratings.size();i++)
        {
          //  cout<<c[i]<<endl;
            result+=c[i];
        }
            return result;
    }
};

406. 根据身高重建队列

假设有打乱顺序的一群人站成一个队列。 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数。 编写一个算法来重建这个队列。


链接:https://leetcode-cn.com/problems/queue-reconstruction-by-height

/*贪心思想:先按身高从大到小排序,先安排身高高的,之后插入,之后的身高都是比他小的,所以插入的位置就是他前面有多少人
           注意身高一样的时候,先安排前面的人数少的那种情况。
*/

bool cmp(vector<int>a, vector<int> b)
{
    if(a[0]>b[0])
        return true;
    else if(a[0]==b[0] && a[1]<b[1])
        return true;
    
    return false;
}
class Solution {
public:
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
     
        vector<vector<int>> res;
        if(people.size()==0)
        {
            return people;
        }
          
        sort(people.begin(),people.end(),cmp);
        int i,j;
        res.push_back(people[0]);
       
        int index; //插入位置
        for(i=1;i<people.size();i++)
        {
            index = people[i][1];
            if(index>res.size())
                res.insert(res.end(),people[i]);
            else
                res.insert(res.begin()+index,people[i]);
            
        }
        return res;
    }
};

392. 判断子序列

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。


链接:https://leetcode-cn.com/problems/is-subsequence

/*贪心思想:直接拿s的字符和t一个一个向后比较,相同则继续(双指针)
*/
class Solution {
public:
    bool isSubsequence(string s, string t) {
     
        if(s=="")
            return true;
        int i=0,j=0;
        while(i<s.size() && j<t.size())
        {
            if(s[i] == t[j])
            {
                i++;
            }
            j++;
        }
        return i==s.size();
    }
};

402. 移掉K位数字

给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。

注意:

num 的长度小于 10002 且 ≥ k。
num 不会包含任何前导零。


链接:https://leetcode-cn.com/problems/remove-k-digits
 

/*贪心思想:如果要剩下的数字最小,那么数字从左开始的每一位都要尽可能地小,因此我们要从剩余数字的高位开始,在有效范围内找到最小的数字。
有效范围就是要除了这个范围剩下的数字的数目是最后要留下来的数目减去已经取了的数字的数目。
比如这个栗子:

输入: num = "1432219", k = 3
要求删除 3 位数字,留下 4 位数字。那么第 1 位数字的查找有效范围是 1432,即下标 0 ~ 3。因为如果我们再往后选择,比如选择第 5 位数字 2,那么我们就无法完整地凑齐 4 位数字了。

*/
class Solution {
public:
    string removeKdigits(string num, int k) {
        
      string res;
      int i; 
      if(num.size() <= k)
      
            return "0" ;
    
      char min_num = num[0];
      int  min_index = 0;
      int last_index= 0;
      int len = num.size() - k;  
        while(len>0)
     {
        if(last_index+len >= num.size())
        {
            res += num.substr(last_index);
            break;
        }
         min_num = num[last_index];
         min_index = last_index;
         for(i=last_index;i<=(num.size()-len);i++)  //有效范围
         {
             
              
             if(num[i] < min_num)
             {
                
                 min_num = num[i] ;   //取范围内最小的数字
                 min_index = i;
             }
         }
         
         last_index = min_index;      //取到的数字的下标,下一次从这个位置的下一个开始取
         
         res+=min_num;
         last_index++;
            len--;
        
     }
        //cout<<res<<endl;
        int j=0;
        int index = 0;
        for(j=0;j<res.size();j++)
        {
            if(res[j]!='0')
            {
                index = j;
                break;
            }
        }
        if(j==res.size())
          index = res.size()-1;
          return res.substr(index); 
    }
};

134. 加油站

在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
链接:https://leetcode-cn.com/problems/gas-station


/* 贪心算法:
   首先要知道两个问题:
      1.当总的耗油量小于总的加油量的时候可以环绕一周
      2.可以环绕一周表示一定有解,要确定开始位置,则一次遍历,如果在i到i+1是到达不了,则起点应该是i+1,不能是i之前的位置,因为i之前的位置已经遍历过,到达i时加油量一定比单纯从他开始的加油量相等 或者比他多,所以是i到i+1的耗油量大,不能选i之前的位置开始。所以start变为i+1

*/
class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        
        int curr_gas = 0;
        int all_gas = 0;
        int i=0;
        int start = 0;
        for(i=0;i<gas.size();i++)
        {
            curr_gas += (gas[i]-cost[i]);
            all_gas += (gas[i]-cost[i]);
            if(curr_gas < 0)
            {
                start = i+1;
                curr_gas = 0;
            }
        }
        
        return all_gas>=0 ? start:-1;
    
    }
};

45. 跳跃游戏 II

 

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。


链接:https://leetcode-cn.com/problems/jump-game-ii
 

/*
  贪心算法: 在第i次的时候,跳跃的范围为i+nums[i],就在该范围内找下一次可以跳到的最远的距离的位置跳
            如果在i位置已经可以跳到终点,则返回当前步数+1(最后跳一次)
            当前跳不到最后,就在可以跳的范围内找下一次可以跳的最远的位置
*/

class Solution {
public:
    int jump(vector<int>& nums) {
        
        int i=0,j;
        int count = 0;
        int cur_max = nums[0];
        while(i<nums.size()-1)
        {
            int after_max = 0;
            int index = 0;
           
            if(i+nums[i] >= nums.size()-1)  //如果在i位置已经可以跳到终点,则返回当前步数+1(最后跳一次)
                    return count+1;
            
            for(j=i+1;j<=i+nums[i];j++)   //当前跳不到最后,就在可以跳的范围内找可以跳的最远的位置
            {

                
                if(j+nums[j] >= after_max)
                {
                    after_max = j+nums[j];  //after_max记录可以跳到的最远的距离,index记录位置,就是i要跳到的位置
                    index = j;
                }
                    

            }
            count++;     //跳的次数加一
            i =index;   //i跳到新的位置
        }
       
        return count;
    }
};

1.

455. 分发饼干

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
链接: https://leetcode-cn.com/problems/assign-cookies

/*
  贪心算法
     把胃口和饼干大小都从小到大排序,尽量用小的饼干满足小胃口的孩子。
*/
class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
      
        sort(g.begin(),g.end());
        sort(s.begin(),s.end());
        int count=0;
        int i,j=0;
        for(i=0;i<g.size();i++)
        {
            while(j<s.size())
            {
                if(g[i]<=s[j])
                {
                    count++;
                    j++;
                    break;
                }
                j++;
            }
        }
        return count;
    }
};

2.

55. 跳跃游戏

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

链接: https://leetcode-cn.com/problems/jump-game/

/*贪心算法:贪当前能够跳的最远的距离
  curr_max表示当前能跳的最远的距离,如果当前是0,还没有到达最后,则false
  关键是curr_max的更新,是目前的位置能跳的最远的
*/
class Solution {
public:
    bool canJump(vector<int>& nums) {
        
        int i=0;
        int curr_max=nums[0];
        for(i=1;i<nums.size();i++)
        {
            if(curr_max == 0)
                return false;
            curr_max = max(curr_max-1,nums[i]);
        }
       return true;
     
    }
};

53. 最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

链接:https://leetcode-cn.com/problems/maximum-subarray/

/*基本思想:一直累加,当和小于等于0的时候,不能再给后面的提供增加,所以重新开始累加,同时累加的过程中如果sum大于已有的max,则更新
*/
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int i;
        int sum=0;
        int max=nums[0];
        for(i=0;i<nums.size();i++)
        {
            sum+=nums[i];
            if(max<sum)
                max=sum;
            if(sum<=0)
                sum=0;
        }
        return max;
    }
};

76. 最小覆盖子串

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。

链接:https://leetcode-cn.com/problems/minimum-window-substring/

/*基本思想:
这道题的思路是:
1) begin开始指向0, end一直后移,直到begin - end区间包含T中所有字符。
记录窗口长度d
2) 然后begin开始后移移除元素,直到移除的字符是T中的字符则停止,此时T中有一个字符没被
包含在窗口,
3) 继续后移end,直到T中的所有字符被包含在窗口,重新记录最小的窗口d。
4) 如此循环知道end到S中的最后一个字符。
时间复杂度为O(n)
*/
class Solution {
public:
    string minWindow(string s, string t) {
        
        
        map<char,int> m;
        int i;
        string res;
        for(i=0;i<t.size();i++)
        {
            if(m.find(t[i])!=m.end())
                m[t[i]]++;
            else
                m[t[i]]=1;
        }
       
        int begin=0;
        int mindis = s.size()+1; 
        int count=t.size();   
       
           
        int end=begin;
        while(end<s.size())
        {
             if(m.find(s[end])!=m.end())
            {
                 if(m[s[end]]>0)
                 {
                    
                     count--;
                 }
                 m[s[end]]--;
                 //cout<<s[end]<<"count"<<count<<endl;
                while(count==0)
                {
                
                if(m.find(s[begin]) != m.end())
                {
                    int dis = end-begin+1;
                    if(dis<mindis){
                        mindis = dis;
                        cout<<dis<<endl;
                        res = s.substr(begin,dis);
                    }
                    
                    
                    m[s[begin]]++;
                    //cout<<"new begin:"<<m[s[begin]]<<endl;
                    if(m[s[begin]]>0)     //此处很精妙!!!maps[S[start]]++代表当前元素被踢出区间,如果区间内不含该元素
                                           //那么这个值就大于0,如果仍然包含这个值那么值就小于等于0
                        count++;
                }
                    begin++;
                }
            }
             end++;        
         }
            
        
    return res;
    }
};

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值