leetcode经典题目(2)--贪心思想

1. 饼干分配(NO.455)

题目描述:假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。一个小朋友最多只能拥有一块饼干。
解题思路:给一个孩子的饼干应当尽量小并且又能满足该孩子,这样大饼干才能拿来给满足度比较大的孩子。因为满足度最小的孩子最容易得到满足,所以先满足满足度最小的孩子。

#include<algorithm>
class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        sort(g.begin(),g.end());//排序
        sort(s.begin(),s.end());
        int i = 0, j = 0;//双指针
        while(i < g.size() && j < s.size()){
            if (g[i] <= s[j]){
                i++;
            }
            j++;
        }
        return i;
    }
};
2. 无重叠区间(NO.435)

题目描述:给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。可以认为区间的终点总是大于它的起点。区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
解题思路:在每次选择中,区间的结尾最为重要,选择的区间结尾越小,留给后面的区间的空间越大,那么后面能够选择的区间个数也就越大。
按区间的结尾进行排序,每次选择结尾最小,并且和前一个区间不重叠的区间。

class Solution {
public:
    static bool cmp(vector<int> &a, vector<int> &b){//按尾元素从小到大排序
        if (a[1] == b[1])
            return a[0] < b[0];
        else
            return a[1] < b[1];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        int n = intervals.size();
        if (n == 0 || n == 1)
            return 0;
        sort(intervals.begin(),intervals.end(),cmp);//排序
        int count = 0;
        vector<int> pre = intervals[0];
        for (int i = 1; i < n; i++){
            if (intervals[i][0] < pre[1])//重叠,pre不变,i要后移
                count++;
            else
                pre = intervals[i];//不重叠,pre和i均后移
        }
        return count;
    }
};
3. 投飞镖刺破气球(NO.452)

题目描述:在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在104个气球。
一支弓箭可以沿着x轴从不同点完全垂直地射出。在坐标x处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。
解题思路:也是计算不重叠的区间个数,不过和 Non-overlapping Intervals 的区别在于,[1, 2] 和 [2, 3] 在本题中算是重叠区间。

class Solution {
public:
    static bool cmp(vector<int> &a, vector<int> &b){
        if (a[1] == b[1])
            return a[0] < b[0];
        else
            return a[1] < b[1];
    }
    int findMinArrowShots(vector<vector<int>>& points) {
        int n = points.size();
        if (n == 0)
            return 0;
        sort(points.begin(),points.end(),cmp);//排序
        vector<int> pre = points[0];
        int count = 1;
        for (int i = 1; i < n; i++){
            if (points[i][0] <= pre[1])//当前区间与前一区间重叠
                continue;
            count++;//不重叠,加1,并移动pre
            pre = points[i];
        }
        return count;
    }
};
4. 根据身高和序号重组队列(NO.406)

题目描述:假设有打乱顺序的一群人站成一个队列。 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数。 编写一个算法来重建这个队列。
输入:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]
输出:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]
解题思路:为了使插入操作不影响后续的操作,身高较高的学生应该先做插入操作,否则身高较小的学生原先正确插入的第 k 个位置可能会变成第 k+1 个位置。
身高 h 降序、个数 k 值升序,然后将某个学生插入队列的第 k 个位置中。对于前面已经排好的队,如果我们在k的位置插入一个身高较低的新人,那么对k之前的人没有任何影响,对于k之后比新人高的人也没有任何影响。

class Solution {
public:
    static bool cmp(vector<int> &a, vector<int> &b){
        if (a[0] == b[0])
            return a[1] < b[1];
        else
            return a[0] > b[0];
    }
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        int n = people.size();
        sort(people.begin(),people.end(),cmp);//按h降序,k升序重排数组
        for (int i = 1; i < n; i++){
            vector<int> temp = people[i];
            for (int j = i - 1; j >= temp[1]; j--){
                people[j+1] = people[j];
            }
            people[temp[1]] = temp;
        }
        return people;
    }
};
5. 买卖股票最大的收益(NO.121)

题目描述:给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。
解题思路:只要记录前面的最小价格,将这个最小价格作为买入价格,然后将当前的价格作为售出价格,查看当前收益是不是最大收益。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        if (n == 0)
            return 0;
        int minPrice = prices[0];
        int max = 0;
        for (int i = 1; i < n; i++){
            if (prices[i] - minPrice > max)
                max = prices[i] - minPrice;
            if (prices[i] < minPrice)
                minPrice = prices[i];
        }
        return max;
    }
};
6. 买卖股票的最大收益 II (NO.122)

题目描述:给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
解题思路:低价买入,至局部最高价卖出,在低价买入,高价卖出。当访问到一个 prices[i] 且 prices[i] - prices[i-1] > 0,那么就把 prices[i] - prices[i-1] 添加到收益中。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        if (n == 0)
            return 0;
        int profit = 0;
        for (int i = 1; i < n; i++){
            if (prices[i] - prices[i-1] > 0)
                profit = profit + prices[i] - prices[i-1];
        }
        return profit;
    }
};
7. 种植花朵(NO.605)

题目描述:假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。给定一个花坛(表示为一个数组包含0和1,其中0表示没种植花,1表示种植了花),和一个数 n 。能否在不打破种植规则的情况下种入 n 朵花?能则返回True,不能则返回False。
解题思路:在数组最前面和最后面添加0,如果某位置的元素为0,且它的前面和后面的元素也都为0,则该位置可以种,计数加1,并将该位置的值改为1,表示此处有花。

class Solution {
public:
    bool canPlaceFlowers(vector<int>& flowerbed, int n) {
        int length = flowerbed.size();
        flowerbed.insert(flowerbed.begin(),0);
        flowerbed.push_back(0);
        int count = 0;
        for (int i = 1; i <= length; i++){
            if (flowerbed[i-1] == 0 && flowerbed[i+1] == 0 && flowerbed[i] == 0){
                count++;
                flowerbed[i] = 1;
            }
        }
        return count >= n;
    }
};
8. 判断是否为子序列(NO.392)

题目描述:给定字符串 s 和 t ,判断 s 是否为 t 的子序列。字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
解题思路:使用双指针,一个指针指向s中的元素,另一个指针指向t中的元素,判断是否相同,若相同,则同时后移,否则,指向t元素的指针后移,最后判断是否遍历完了s中的元素。

class Solution {
public:
    bool isSubsequence(string s, string t) {
        int length1 = s.size();
        int length2 = t.size();
        int i = 0, j = 0;//双指针
        while(i < length1 && j < length2){
            if (s[i] == t[j])
                i++;
            j++;
        }
        return i == length1;
    }
};
9. 修改一个数成为非递减数组(NO.665)

题目描述:给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。我们是这样定义一个非递减数列的: 对于数组中所有的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。
解题思路:若nums[i]<nums[i-1],有两种选择,第一种,把nums[i-1]变小,即让nums[i-1]=nums[i];第二种,让nums[i]变大,即让nums[i]=nums[i-1],但是这样会影响后续过程,只有当nums[i]<nums[i-2]时,才会这样选择。

class Solution {
public:
    bool checkPossibility(vector<int>& nums) {
        int cnt = 0;
        for (int i = 1; i < nums.size() && cnt < 2; i++){
            if (nums[i] >= nums[i-1])
                continue;
            cnt++;
            if (i-2 >= 0 && nums[i] < nums[i-2])
                nums[i] = nums[i-1];//把nums[i]变大
            else
                nums[i-1] = nums[i];//把nums[i-1]变小
        }
        return cnt <= 1;
    }
};
10. 子数组最大的和(NO.53)

题目描述:给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。例如对于数组 [-2,1,-3,4,-1,2,1,-5,4],其连续子数组 [4,-1,2,1] 的和最大,为 6。
解题思路:动态规划。使用preSum记录以第i-1个元素为结尾的连续子数组的最大和。如果preSum>0,则更新preSum为num[i]+preSum,如果preSum<0,直接更新preSum为num[i],使用maxSum记录最大和。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int preSum = nums[0];
        int maxSum = nums[0];
        for (int i = 1; i < nums.size(); i++){
            preSum = preSum > 0 ? nums[i] + preSum : nums[i];
            maxSum = max(preSum,maxSum);
        }
        return maxSum;
    }
};
11. 分隔字符串使同种字符出现在一起(NO.763)

题目描述:字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一个字母只会出现在其中的一个片段。返回一个表示每个字符串片段的长度的列表。
输入:S = “ababcbacadefegdehijhklij”;划分结果为 “ababcbaca”, “defegde”, “hijhklij”。
输出:[9,7,8]
解题思路:用一个数组lastIndexsOfchar[]记录各个字符在字符串中最后出现的位置。firstIndex指向一个片段的首位置,lastIndex指向该片段的尾位置,遍历之间所有元素,同时更新尾位置,直至遍历到尾位置。

class Solution {
public:
    vector<int> partitionLabels(string S) {
        vector<int> lastIndexsOfchar(26,0);
        for (int i = 0; i < S.size(); i++){
            lastIndexsOfchar[charToIndex(S[i])] = i;
        }
        int firstIndex = 0;
        vector<int> result;
        while (firstIndex < S.size()){
            int lastIndex = firstIndex;
            for (int  i = firstIndex; i < S.size() && i <= lastIndex; i++){
                int index = lastIndexsOfchar[charToIndex(S[i])];
                if (index > lastIndex)
                    lastIndex = index;
            }
            result.push_back(lastIndex-firstIndex+1);
            firstIndex = lastIndex + 1;
        }
        return result;
    }

    int charToIndex(char c){
        return c - 'a';
    }
};
12. 移除k位数字,使字符串数字最小

题目描述:给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。
解题思路:两个相同位数的数字大小关系取决于第一个不同的数的大小。遍历字符串,如果发现前一个数字大于后一个数字,则将前一个数字删除,使得高位数字尽可能小。

class Solution {
public:
    string removeKdigits(string num, int k) {
        if (k == num.size())
            return "0";
        while (k){
            int i;
            bool flag = false;
            //遍历查找有没有前一个数大于后一个数的情况
            for (i = 0; i < num.size() - 1; i++){
                if (num[i] > num[i+1]){
                    num.erase(num.begin() + i);
                    flag = true;
                    k--;
                    break;
                }
            }
            //遍历完数组,而没有删除元素,说明字符串有序,直接删除后K个
            if (i == num.size() - 1 && !flag){
                num.erase(num.end()-k,num.end());
                break;
            }
        }
        while (num[0] == '0' && num.size() > 1)
                num.erase(num.begin());
        if (num.empty())
            return "0";
        return num;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值