LeetCode 贪心

基础部分

455. 分发饼干

简单

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

注意:

你可以假设胃口值为正。
一个小朋友最多只能拥有一块饼干。

示例 1:

输入: [1,2,3], [1,1]

输出: 1

解释: 
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。

示例 2:

输入: [1,2], [1,2,3]

输出: 2

解释: 
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.
class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int res = 0;
        int i = g.length-1;
        int j = s.length-1;
        while (i >= 0 && j >=0){
            if (s[j] >= g[i]){ //满足了拿去吃
                res++;
                j--;
            }
            i--;
        }
        return res;
    }
}

435. 无重叠区间

中等

给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

注意:

  1. 可以认为区间的终点总是大于它的起点。
  2. 区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。

示例 1:

输入: [ [1,2], [2,3], [3,4], [1,3] ]

输出: 1

解释: 移除 [1,3] 后,剩下的区间没有重叠。

示例 2:

输入: [ [1,2], [1,2], [1,2] ]

输出: 2

解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。

示例 3:

输入: [ [1,2], [2,3] ]

输出: 0

解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        int len = intervals.length;
        if (len < 2) return 0;
        Arrays.sort(intervals,(a,b)->(a[0]-b[0])); //以第一位升序排列
        List<int[]> list = new LinkedList<>();
        list.add(intervals[0]);
        for (int i = 0; i < intervals.length; i++) {
            int tail = list.get(list.size()-1)[1];
            if (tail > intervals[i][0]){
                if (tail > intervals[i][1]) //留尾巴短的,后边不易重叠
                    list.get(list.size()-1)[1] = intervals[i][1];
            }else list.add(intervals[i]);
        }
        return len - list.size();
    }
}

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

中等

在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在104个气球。

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

Example:

输入:
[[10,16], [2,8], [1,6], [7,12]]

输出:
2

解释:
对于该样例,我们可以在x = 6(射爆[2,8],[1,6]两个气球)和 x = 11(射爆另外两个气球)。
class Solution {
    public int findMinArrowShots(int[][] points) {
        if (points.length < 2) return points.length;
        Arrays.sort(points,(a,b)->(a[0]-b[0]));
        int res = 0;
        int i = 0;
        while (i < points.length){
            res++;
            int last = points[i][1];
            do {
                i++;
                if (i < points.length && points[i][1] < last) 
                    last = points[i][1]; //整体的尾巴往前才能都中
            }while (i < points.length && points[i][0] <= last);
        }
        return res;
    }
}

406. 根据身高重建队列

中等

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

注意:
总人数少于1100人。

示例

输入:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]

输出:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]
class Solution {
    public int[][] reconstructQueue(int[][] people) {
        Arrays.sort(people,(a,b)->a[1]-b[1]); //第二位正序
        Arrays.sort(people,(a,b)->b[0]-a[0]); //第一位倒序
        for (int i = 0; i < people.length; i++)
            if (people[i][1] != i) { //people[i][1]:该去的位置
                int j = i;
                int[] tmp = people[j];
                while (j != tmp[1]) { //tmp很关键,因为i和j都变化了,必须tmp
                    people[j] = people[j - 1];
                    j--;
                }
                people[j] = tmp;
            }
        return people;
    }
}

121. 买卖股票的最佳时机

简单

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

注意:你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
class Solution {
    public int maxProfit(int[] prices) {
        int res = 0;
        int prof = 0;
        for (int i = 1; i < prices.length; i++) {
            int minus = prices[i] - prices[i-1];
            prof += minus;
            if (prof < 0) prof = 0; //负收益还不如不买
            else if (prof > res) res = prof;
        }
        return res;
    }
}

122. 买卖股票的最佳时机 II

简单

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

示例 2:

输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:

  • 1 <= prices.length <= 3 * 10 ^ 4
  • 0 <= prices[i] <= 10 ^ 4
class Solution {
    public int maxProfit(int[] prices) {
        int prof = 0;
        for (int i = 1; i < prices.length; i++) {
            int minus = prices[i] - prices[i-1];
            prof += minus > 0 ? minus : 0;
        }
        return prof;
    }
}

605. 种花问题

简单

假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。

给定一个花坛(表示为一个数组包含0和1,其中0表示没种植花,1表示种植了花),和一个数 n 。能否在不打破种植规则的情况下种入 n 朵花?能则返回True,不能则返回False。

示例 1:

输入: flowerbed = [1,0,0,0,1], n = 1
输出: True

示例 2:

输入: flowerbed = [1,0,0,0,1], n = 2
输出: False

注意:

  1. 数组内已种好的花不会违反种植规则。
  2. 输入的数组长度范围为 [1, 20000]。
  3. n 是非负整数,且不会超过输入数组的大小。
class Solution {
    public boolean canPlaceFlowers(int[] flowerbed, int n) {
        int k = 0;
        for (int i = 0; i < flowerbed.length && k<n; i++) {
            if (flowerbed[i] == 0){
                if (i+1 == flowerbed.length || flowerbed[i+1] == 0){
                    flowerbed[i] = 1;
                    k++;
                    i++; //下一个地方肯定不能种,跳过
                }
            }else {
                i++; //下一个地方肯定不能种,跳过
            }
        }
        return k >= n;
    }
}

392. 判断子序列

简单

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

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

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

示例 1:
s = "abc", t = "ahbgdc"

返回 true.

示例 2:
s = "axc", t = "ahbgdc"

返回 false.

后续挑战 :

如果有大量输入的 S,称作S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

class Solution {
    public boolean isSubsequence(String s, String t) {
        char[] schars = s.toCharArray();
        char[] tchars = t.toCharArray();
        int i, j;
        for (i = 0,j = 0; i < schars.length; i++) {
            while (j < tchars.length && tchars[j] != schars[i]) j++;
            if (j >= tchars.length) break;
            j++;
        }
        return i == schars.length;
    }
}

665. 非递减数列

简单

给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。

我们是这样定义一个非递减数列的: 对于数组中所有的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]

示例 1:

输入: nums = [4,2,3]
输出: true
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。

示例 2:

输入: nums = [4,2,1]
输出: false
解释: 你不能在只改变一个元素的情况下将其变为非递减数列。

说明:

  • 1 <= n <= 10 ^ 4
  • - 10 ^ 5 <= nums[i] <= 10 ^ 5
class Solution {
    public boolean checkPossibility(int[] nums) {
        int fix = 0;
        for (int i = 1; i < nums.length && fix <= 1; i++){
            if (nums[i] < nums[i-1]){
                fix++;
                if (i-2 >= 0 && nums[i]<nums[i-2]){ //突然有个数字小了
                    nums[i]=nums[i-1];
                }else { //前边的数字小了
                    nums[i-1]=nums[i];
                }
            }
        }
        return fix <= 1;
    }
}

53. 最大子序和

简单

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

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

进阶:

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

class Solution {
    public int maxSubArray(int[] nums) {
        int res = Integer.MIN_VALUE;
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
            res = res > sum ? res : sum;
            sum = sum < 0 ? 0 : sum;
        }
        return res;
    }
}

763. 划分字母区间

中等

字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一个字母只会出现在其中的一个片段。返回一个表示每个字符串片段的长度的列表。

示例 1:

输入:S = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。

提示:

  • S的长度在[1, 500]之间。
  • S只包含小写字母 'a''z'
class Solution {
    public List<Integer> partitionLabels(String S) {
        char[] chars = S.toCharArray();
        Map<Character,Integer> map = new HashMap<>();
        for (int i = 0; i < chars.length; i++) {
            map.put(chars[i],i);
        }
        List<Integer> list = new LinkedList<>();
        int max = 0;
        int i = 0;
        int j = 0;
        while (j < chars.length){
            max = Math.max(max,map.get(chars[j]));
            if (j++ == max) {
                list.add(j-i);
                i = j;
            }
        }
        return list;
    }
}

频率排序

135. 分发糖果

困难

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

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

  • 每个孩子至少分配到 1 个糖果。
  • 相邻的孩子中,评分高的孩子必须获得更多的糖果。

那么这样下来,老师至少需要准备多少颗糖果呢?

示例 1:

输入: [1,0,2]
输出: 5
解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。

示例 2:

输入: [1,2,2]
输出: 4
解释: 你可以分别给这三个孩子分发 1、2、1 颗糖果。
     第三个孩子只得到 1 颗糖果,这已满足上述两个条件。
class Solution {
    public int candy(int[] ratings) {
        int len = ratings.length;
        int[] res = new int[len];
        res[0] = 1;
        for (int i = 1; i < len; i++) {
            if (ratings[i] == ratings[i-1]) res[i] = 1;
            else if (ratings[i] > ratings[i-1]) res[i] = res[i-1]+1;
            else { //发现前面的孩子给少了
                res[i] = 1;
                int j = i;
                do {
                    j--;
                    if (res[j] <= res[j+1]){
                        res[j] = res[j+1] + 1;
                    }else {
                        break;
                    }
                }while (j > 0 && ratings[j] < ratings[j-1]);
            }
        }
        int candys = 0;
        for (int candy : res) candys += candy;
        return candys;
    }
}

316. 去除重复字母

困难

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

示例 1:

输入: "bcabc"
输出: "abc"

示例 2:

输入: "cbacdcbc"
输出: "acdb"
public class Solution {
   public String removeDuplicateLetters(String s) {
       int[] cnt = new int[26];
       int pos = 0;
       for (int i = 0; i < s.length(); i++) //字母出现次数的字典
           cnt[s.charAt(i) - 'a']++;
       for (int i = 0; i < s.length(); i++) {
           if (s.charAt(i) < s.charAt(pos)) pos = i; //后边的比pos的字典顺序小
           if (--cnt[s.charAt(i) - 'a'] == 0) break; //字典里减没了,pos可以放第一位
       }
       return s.length() == 0 ? "" : s.charAt(pos) + 
               removeDuplicateLetters(s.substring(pos + 1) //递归
               .replaceAll("" + s.charAt(pos), "") //这个字符统计完了,全部去掉
       ); 
   }
}

45. 跳跃游戏 II

困难

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

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

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

示例:

输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

说明:

假设你总是可以到达数组的最后一个位置。

class Solution {
    public int jump(int[] nums) {
        int res = 0;
        for (int i = nums.length - 1; i > 0; ) {
            int pos = i - 1;
            for (int j = 0; j < i; j++)
                if (nums[j] >= i-j) pos = Math.min(pos,j);
            res++;
            i = pos;
        }
        return res;
    }
}

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

中等

给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

示例 1:

输入: prices = [1, 3, 2, 8, 4, 9], fee = 2
输出: 8
解释: 能够达到的最大利润:  
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8.

注意:

  • 0 < prices.length <= 50000.
  • 0 < prices[i] < 50000.
  • 0 <= fee < 50000.
//未通过,不知道为什么
class Solution {
    public int maxProfit(int[] prices, int fee) {
        int sum = 0;
        int[] profs = new int[prices.length-1];
        for (int i = 0; i < profs.length; i++)
            profs[i] = prices[i+1] - prices[i];
        int prof = 0;
        int drop = 0;
        for (int i = 0; i < profs.length; i++) {
            if (prof == 0 && profs[i] < 0) continue;
            if (profs[i] < 0){
                drop += profs[i];
                if (drop <= -fee) { //赔fee块钱,还不如交管理费卖了
                    if (prof > fee) sum += prof-fee;
                    drop = 0;
                    prof = 0;
                }
            }else {
                prof += drop + profs[i];
                drop = 0;
            }
        }
        sum += prof > fee ? prof - fee : 0; //最后一波没加上
        return sum;
    }
}

402. 移掉K位数字

中等

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

注意:

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

示例 1 :

输入: num = "1432219", k = 3
输出: "1219"
解释: 移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219。

示例 2 :

输入: num = "10200", k = 1
输出: "200"
解释: 移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。

示例 3 :

输入: num = "10", k = 2
输出: "0"
解释: 从原数字移除所有的数字,剩余为空就是0。
class Solution {
    public String removeKdigits(String num, int k) {
        LinkedList<Character> stack = new LinkedList<>();

        for(char digit : num.toCharArray()) {
            while(!stack.isEmpty() && k > 0 && stack.peekLast() > digit) {
                //stack不为空,k大于0,前面的数大于后面的数
                stack.removeLast(); //去掉前面的数
                k -= 1; //去掉一次少一次机会
            }
            stack.addLast(digit);
        }
        
        for(int i=0; i<k; ++i) stack.removeLast(); //还有机会没用完
        
        StringBuilder res = new StringBuilder();
        while (stack.peekFirst() == '0') stack.pollFirst();
        for (Character c : stack) res.append(c);

        return res.length() > 0 ? res.toString() : "0";
    }
}

1029. 两地调度

简单

公司计划面试 2N 人。第 i 人飞往 A 市的费用为 costs[i][0],飞往 B 市的费用为 costs[i][1]

返回将每个人都飞到某座城市的最低费用,要求每个城市都有 N 人抵达

示例:

输入:[[10,20],[30,200],[400,50],[30,20]]
输出:110
解释:
第一个人去 A 市,费用为 10。
第二个人去 A 市,费用为 30。
第三个人去 B 市,费用为 50。
第四个人去 B 市,费用为 20。

最低总费用为 10 + 30 + 50 + 20 = 110,每个城市都有一半的人在面试。

提示:

  1. 1 <= costs.length <= 100
  2. costs.length 为偶数
  3. 1 <= costs[i][0], costs[i][1] <= 1000
class Solution {
    public int twoCitySchedCost(int[][] costs) {
        int potA = costs.length/2;
        int potB = costs.length/2;
        Arrays.sort(costs,(x, y)->Math.abs(y[0]-y[1])-Math.abs(x[0]-x[1])); //相差越多权重越高,,权重高的人先挑
        int res = 0;
        for (int i = 0; i < costs.length; i++) {
            int a = costs[i][0];
            int b = costs[i][1];
            if (a < b) {
                if (potA-- > 0) res += a;
                else res += b;
            } else {
                if (potB-- > 0) res += b;
                else res += a;
            }
        }
        return res;
    }
}
//官方的,(A地-B地)的升序,这么简单吗,凭什么
class Solution {
    public int twoCitySchedCost(int[][] costs) {
        Arrays.sort(costs,(a,b)->a[0]-a[1]-b[0]+b[1]);
        int total = 0;
        int n = costs.length / 2;
        for (int i = 0; i < n; ++i) total += costs[i][0] + costs[i + n][1];
        return total;
    }
}
posted @ 2020-07-25 14:52  鹏懿如斯  阅读(1090)  评论(0编辑  收藏  举报