leetcode刷题笔记

目录

1、贪心算法的概念

2、需要满足的条件

3、基本的步骤

4、leetcode上的题目

分饼干问题

活动安排问题

摆动序列

盛最多水的容器

跳跃游戏

跳跃游戏II

股票买卖的最佳时机

最长回文串

移掉k位数字

最大数

加油站


(一)贪心

1、贪心算法的概念

  • 贪心算法(Greedy Alogorithm),即逐步获得最优解,原理是通过局部最优来达到全局最优,是用来解决最优问题的,但其适用范围有限,在每一步的选择中,只考虑当前对自己最有利的选择
  • 没有固定的框架,其核心是贪心策略的选择
  • 通过局部最优来达到全局最优,贪心策略的选择不是一件容易的事情,要想证明局部最优为全局最优,还需要通过数学证明,证明其是否具有贪心选择性质,否则不能说明其为全局最优。

2、需要满足的条件

  • 最优子结构:规模较大的问题的解由规模较小的子问题的解组成,规模较大的问题的解只由其中一个规模较小的子问题的解决定;
  • 无后效性:后面阶段的求解不会修改前面阶段已经计算好的结果;
  • 贪心选择性质:从局部最优解可以得到全局最优解。

3、基本的步骤

  1. 从问题的某个初始解出发
  2. 采用循环语句,当可以向求解目标前进一步时,就根据局部最优解策略,得到一个部分解,缩小问题的规模
  3. 将所有的部分解综合起来,得到问题的最终解
  4. Greedy(C)  //C是问题的输入集合即候选集合
    {
        S={ };  //初始解集合为空集
        while (not solution(S))  //集合S没有构成问题的一个解
        {
           x=select(C);    //在候选集合C中做贪心选择
           if feasible(S, x)  //判断集合S中加入x后的解是否可行
              S=S+{x};
              C=C-{x};
        }
       return S;
    

4、leetcode上的题目

分饼干问题

455. 分发饼干 - 力扣(Leetcode)

class Solution {
public:
    int findContentChildren(vector<int>& g,vector<int>& s){//g为胃口大小,s为饼干大小
        sort(g.begin(),g.end());//给胃口排序
        sort(s.begin(),s.end());//给饼干排序
        int gm=g.size();//胃口个数
        int sm=s.size();//饼干个数
        int count=0;//答案计数
        int i,j;
        for(i=0,j=0;i<gm&&j<sm;i++,j++){//i为饥饿度的指针,j为饼干的指针
            while(j<sm&&g[i]>s[j])//j一直移动直到找到满足条件的饼干,当前的饼干满足条件
            j++;
            if(j<sm)//避免上面的j越界
                count++;//计数器加1,进入下一个for循环时会跳入下一个饼干
        }
    return count;//输出最大的饼干数
    }
};

这里的贪心策略为:应该按照孩子的胃口从小到大的顺序依次满足每个孩子,且对于每个孩子,应该选择可以满足这个孩子的胃口且尺寸最小的饼干。

活动安排问题

贪心算法之活动安排问题

这里的贪心策略为:就是找出最早完成的活动,然后按照活动相容的条件(上一个活动的结束时间早于下一个活动的开始时间)依次往后寻找。需要将所有活动按照结束时间进行非降序的排序,依次排列遍历找出最大的相容活动子集合。

摆动序列

376. 摆动序列 - 力扣(Leetcode)

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        if(nums.size()<2)return nums.size();
        int prevdiff=nums[1]-nums[0];//记录前两个数的升降情况,两个数相同则“平缓”,只算一个节点
        int res= prevdiff != 0 ? 2 : 1;//两个数不相同则开始有两个节点
        for(int i=2;i<nums.size();i++){
            int diff=nums[i]-nums[i-1];//记录当前的节点的升降趋势
            if((diff>0&&prevdiff<=0)||(diff<0&&prevdiff>=0)){//检查是否为峰谷
                res++;
                prevdiff=diff;//更新趋势
            }
        }
        return res;
    }
};


这里的贪心策略为:不断地交错选择「峰」与「谷」,可以使得该序列尽可能长。每次加入一个新元素时,用新的上升下降趋势与之前对比,如果出现了「峰」或「谷」,答案加一,并更新当前序列的上升下降趋势。

盛最多水的容器

盛最多水的容器 - 力扣(Leetcode)

class Solution {
public:
    int maxArea(vector<int>& height) {
        int i = 0, j = height.size() - 1, res = 0;//i从左边开始,j从右边开始
        while(i < j) {//指针相遇时停下
            res = height[i] < height[j] ? //找短板,移动长板s一定会变小,移动短板可能会变大
                max(res, (j - i) * height[i++]): //看是否需要更新最大面积
                max(res, (j - i) * height[j--]); 
        }
        return res;
    }
};

这里的贪心策略为:每次都去找短板,移动长板s一定会变小(目前最优解),移动短板s可能会变大。

跳跃游戏

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int n=nums.size();//数组长度
        int rightmost=0;//最远距离
        for(int i=0;i<n;i++){//遍历每一个数的最远距离
            if(i<=rightmost){//当前的i还可到达则执行
                rightmost=max(rightmost,i+nums[i]);//更新最远距离
                if(rightmost>=n-1)
                return true;//可以到达最后一个数
            }
        }
        return false;//不可以到达
    }
};

这里的贪心策略为:遍历数组中的每一个元素,对于每一个元素,求出其可到达的最远距离。

跳跃游戏II

class Solution {
public:
    int jump(vector<int>& nums) {
        int maxPos = 0, n = nums.size(), end = 0, step = 0;//最大跳数,长度,边界,最短要跳
        for (int i = 0; i < n - 1; ++i) {//遍历每一个元素
            if (maxPos >= i) {
                maxPos = max(maxPos, i + nums[i]);//更新能够到达的最大下标位置
                if (i == end) {
                    end = maxPos;//区间上的能够到达的最大下标位置作为下一个边界
                    ++step;
                }
            }
        }
        return step;
    }
};
//当最后一个恰好为边界时,会增加一跳,所以不要

解析:跳跃游戏 II - 力扣(Leetcode)

这里的贪心策略为:从第一个数开始遍历数组,根据跳数确定一个区间,区间的右边界,遍历区间,在区间上确定最大跳数的元素,将最跳数更新为这个跳数,遍历完区间时,将最大跳数作为下一个边界。

股票买卖的最佳时机

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len=prices.size();
        int max=0;//总利润
        for(int i=1;i<len;i++){
            int temp=prices[i]-prices[i-1];//第 i-1 日买入与第 i 日卖出赚取的利润
            if(temp>0){//有钱赚
                max+=temp;
            }
        }
        return max;
    }
};

这里的贪心策略为:第 i-1 日买入与第 i 日卖出赚取的利润,只看这两天有没有利润,局部最优

最长回文串

class Solution {
public:
    int longestPalindrome(string s) {
        unordered_map<char,int>count;//创建一个哈希表count
        int ans=0;//计数器
        for(char c:s){//从s的第一个字符开始,往后循环
            ++count[c];给ke为c的字符的value +1
        }
        for(auto p:count){//auto用来简化初始化p,根据后面的count可知p为哈希表类型
            int v=p.second;//拿出当前的value
            ans+=v/2*2;//每次都要拿最长的
            if(v%2==1&&ans%2==0)//遇到第一个个数为奇数的字符串时,ans++,作为最中间的轴
            ans++;
        }
        return ans;
    }
};

这里的贪心策略为:每次都要拿最长的

移掉k位数字

class Solution {
public:
    string removeKdigits(string num, int k) {
        vector<int> s;
        string result="";
        for(int i=0;i<num.length();i++){
            int number=num[i]-'0';//转化成整数
            while(s.size()!=0&&k>0&&s[s.size()-1]>number){//必须用while,遇到“1230”
                s.pop_back();
                k--;
            }
            if(s.size()!=0||number!=0){
                s.push_back(number);
            }
        }
        while(s.size()!=0&&k>0){
                s.pop_back();
                k--;
        }
        for(int i=0;i<s.size();i++){
            result.append(1,s[i]+'0');//在后面加“1”个字符
        }
        if(result=="") result="0";
        return result;
    }
};

这里的贪心策略为:从高位向地位遍历,如果对应的数字大于下一位数字,则把该位数字去掉,得到的数字最小,使得每去掉一位都能得到当前最小的数字。

最大数

class Solution {
public:
    string largestNumber(vector<int>& nums) {
        vector<string> str;
        for(auto i : nums) {
            str.push_back(to_string(i));//将数字常量转换成字符串后放入str
        }
        // 使用 lambda 比较 elements.
        auto cmp = [](string left, string right) {
            return left + right > right + left;
        };
        sort(str.begin(),str.end(), cmp);
        //定义一个对象ss
        stringstream ss;
        for(auto c : str) {
            ss << c;//把c放入ss中
        }
        string ans = ss.str();//ss中的全部的字符取出
        if(ans[0] == '0'){
            return "0";
        }
        return ans;
    }
};

贪心策略:根据拼接的「结果」来决定 a和 b的排序关系:如果拼接结果 ab要比 ba好,那么我们会认为 a应该放在 b前面。按照这种规则把数组中的值重新排序。

加油站

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) 
	{
		int start=0;// 从start出发 
		int spare=0; //从start出发的话到当前位置的油量
		int sum=0;  //记录总和
		for(int i=0;i<gas.size();i++)
		{
			spare+=gas[i]-cost[i]; //
			sum+=gas[i]-cost[i];
			if(spare<0)  //spare<0说明从start开始不满足,将start更新为当前位置的下一个位置
			{
				start=i+1;	
				spare=0;
			}	
		}
		return (sum<0)?-1:(start); 
    }
};

贪心策略:如果总油量减去总消耗大于等于零那么一定可以跑完一圈,因此要跑完一圈就要保证在各个站点的剩油量>=0。 局部最优:若当前累加到 j 的和 Sum<0 ,起始位置至少要是 j+1,因为从 j开始一定不行。 全局最优:找到可以跑一圈的起始位置。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值