Loading

Leedcode算法专题训练(贪心)

1. 分配饼干

455. 分发饼干

题目描述:每个孩子都有一个满足度 grid,每个饼干都有一个大小 size,只有饼干的大小大于等于一个孩子的满足度,该孩子才会获得满足。求解最多可以获得满足的孩子数量。

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int i=0,j=0;
        while(i<g.length && j<s.length){
            if(g[i]<=s[j]){
                i++;
                j++;
            }
            else{
                j++;
            }
        }
        return i;
    }
}

2. 不重叠的区间个数

435. 无重叠区间

这个是经典的贪心算法,典型的不能再典型

先计算最多能组成的不重叠区间个数,然后用区间总个数减去不重叠区间的个数。

核心思想就是选结束时间最早的。

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {  
        int n = intervals.length;
        return n - intervalSchedule(intervals);
    }
    public int intervalSchedule(int[][] intvs){
        if(intvs.length==0)return 0;
        Arrays.sort(intvs,new Comparator<int[]>(){
            public int compare(int[] a,int[] b){
                return a[1]-b[1];
            }
        });
        int count=1;
        int x_end=intvs[0][1];
        for(int[] interval:intvs){
            int start=interval[0];
            if(start>=x_end){
                count++;
                x_end=interval[1];
            }
        }
        return count;
    }
}

学习一下lamada表达式 

注意大数的问题

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {  
        int n = intervals.length;
        return n - intervalSchedule(intervals);
    }
    public int intervalSchedule(int[][] intvs){
        if(intvs.length==0)return 0;
        Arrays.sort(intvs, (a,b)->(a[1]-b[1]));
        int count=1;
        int x_end=intvs[0][1];
        for(int[] interval:intvs){
            int start=interval[0];
            if(start>=x_end){
                count++;
                x_end=interval[1];
            }
        }
        return count;
    }
}

3. 投飞镖刺破气球

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

题目描述:气球在一个水平数轴上摆放,可以重叠,飞镖垂直投向坐标轴,使得路径上的气球都被刺破。求解最小的投飞镖次数使所有气球都被刺破。

也是计算不重叠的区间个数,不过和 Non-overlapping Intervals 的区别在于,[1, 2] 和 [2, 3] 在本题中算是重叠区间。

 

Arrays.sort(intervals, Comparator.comparingInt(o -> o[1]));
class Solution {
    public int findMinArrowShots(int[][] points) {
        int n=points.length;
        if(n==0)return 0;
        Arrays.sort(points,(a,b)->(a[1]-b[1]));
        int count=1;
        int end=points[0][1];
        for(int[] arr:points){
            int start=arr[0];
            if(start>end){
                count++;
                end=arr[1];
            }
        }
        return count;
    }
}

4. 根据身高和序号重组队列

注意点为高个子是无法注意矮个子的,并且矮个子无法改变高个子的身高索引数值k

身高 h 降序、个数 k 值升序,然后将某个学生插入队列的第 k 个位置中。

406. 根据身高重建队列

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        if(people==null||people.length==0||people[0].length==0){
            return new int[0][0];
        }
        Arrays.sort(people,(a,b)->(a[0]==b[0]?a[1]-b[1]:b[0]-a[0]));
        List<int[]>queue=new ArrayList<>();
        for(int[] p:people){
            queue.add(p[1],p);
        }
        return queue.toArray(new int[queue.size()][]);

    }
}

5. 买卖股票最大的收益

class Solution {
    public int maxProfit(int[] prices) {
        int max=0;
        for(int i=0;i<prices.length;i++){
            for(int j=i;j<prices.length;j++){
                int index=prices[j]-prices[i];
                if(max<index)max=index;
            }
        }
        return max;
    }
}

只要记录前面的最小价格,将这个最小价格作为买入价格,然后将当前的价格作为售出价格,查看当前收益是不是最大收益。

public int maxProfit(int[] prices) {
    int n = prices.length;
    if (n == 0) return 0;
    int soFarMin = prices[0];
    int max = 0;
    for (int i = 1; i < n; i++) {
        if (soFarMin > prices[i]) soFarMin = prices[i];
        else max = Math.max(max, prices[i] - soFarMin);
    }
    return max;
}

6. 买卖股票的最大收益 II

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

对于 [a, b, c, d],如果有 a <= b <= c <= d ,那么最大收益为 d - a。而 d - a = (d - c) + (c - b) + (b - a) ,因此当访问到一个 prices[i] 且 prices[i] - prices[i-1] > 0,那么就把 prices[i] - prices[i-1] 添加到收益中。

class Solution {
    public int maxProfit(int[] prices) {
        if(prices.length==0)return 0;
        int max = 0;
        for(int i = 1; i < prices.length; i++){
            if(prices[i]>prices[i - 1]){
                max += prices[i] - prices[i - 1];
            }
        }
        return max;
        
    }
}

7. 种植花朵

605. 种花问题

public class Solution {
    public boolean canPlaceFlowers(int[] flowerbed, int n) {
        int i = 0, count = 0;
        while (i < flowerbed.length) {
            if (flowerbed[i] == 0 && (i == 0 || flowerbed[i - 1] == 0) && (i == flowerbed.length - 1 || flowerbed[i + 1] == 0)) {
                flowerbed[i] = 1;
                count++;
            }
            i++;
        }
        return count >= n;
    }
}

8. 判断是否为子序列

392. 判断子序列

class Solution {
    public boolean isSubsequence(String s, String t) {
        if(s.length()==0)return true;
        int i = 0,j = 0;
        while(j<t.length() && i<s.length()){          
            if(s.charAt(i)==t.charAt(j)){
                i++;
            }
            j++;
        }
        if(i==s.length())return true;
        return false;
    }
}
public boolean isSubsequence(String s, String t) {
    int index = -1;
    for (char c : s.toCharArray()) {
        index = t.indexOf(c, index + 1);
        if (index == -1) {
            return false;
        }
    }
    return true;
}

9. 修改一个数成为非递减数组

665. 非递减数列

思路要清洗,具体分析多种情况

  这道题给了我们一个数组,说我们最多有1次修改某个数字的机会,
  问能不能将数组变为非递减数组。题目中给的例子太少,不能覆盖所有情况,我们再来看下面三个例子:
    4,2,3
    -1,4,2,3
    2,3,3,2,4
我们通过分析上面三个例子可以发现,当我们发现后面的数字小于前面的数字产生冲突后,

  1. 有时候需要修改前面较大的数字(比如前两个例子需要修改4),
  2. 有时候却要修改后面较小的那个数字(比如前第三个例子需要修改2),

那么有什么内在规律吗?是有的,判断修改那个数字其实跟再前面一个数的大小有关系,
首先如果再前面的数不存在,比如例子1,4前面没有数字了,我们直接修改前面的数字为当前的数字2即可。
而当再前面的数字存在,并且小于当前数时,比如例子2,-1小于2,我们还是需要修改前面的数字4为当前数字2;
如果再前面的数大于当前数,比如例子3,3大于2,我们需要修改当前数2为前面的数3。

class Solution {
    public boolean checkPossibility(int[] nums) {
        int cnt=0;
        for(int i=1; i < nums.length && cnt < 2; i++){
            if(nums[i]>=nums[i-1])continue;
            cnt++;
            if(i-2>=0 && nums[i-2] > nums[i]){
                nums[i]=nums[i-1];
            }
            else{
                nums[i-1]=nums[i];
            }
        }
        return cnt <= 1;
    }
}

10. 子数组最大的和

53. 最大子序和

梦开始的题

class Solution {
    public int maxSubArray(int[] nums) {
        if(nums == null ||nums.length == 0){
            return 0;
        }
        int presum=nums[0];
        int maxsum=presum;
        for(int i=1; i<nums.length; i++){
            presum=presum>0?presum+nums[i]:nums[i];
            maxsum=Math.max(maxsum,presum);
        }
        return maxsum;

    }
}

思路
假设[i,j]为我们所寻找的子序列
nums[left,right]考虑任何连续子数组nums[i,j]必然位于下列三种情况 method(getMaxSubArray(int[] nums,int left,int right)
1. 完全位于子数组nums[left,mid]中,因此left<=i<=j<=mid
2. 完全位于子数组nums[mid+1,right]中,因此mid+1<=i<=j<=right
3. 跨越了中点,因此left<=i<=mid<=j<=right
4. 比较三种情况 返回最大值
关键在于情况3 如何对于跨越中点子序列求解(其实并不难) 其实就是两侧最大子序列和相加 method public int midSumMax(int left,int mid,int right,int[] nums)
1. 求出以mid为定点,从mid down to left 的子序列 [i,mid] 也就是leftMaxSum;
2. 求出以mid+1为定点,从mid+1 up to right 的子序列 [mid+1,j] 也就是RightMaxSum;
3. 求和leftMaxSum+RightMaxSum 从而得出中间最大子序列和

class Solution {
   public int maxSubArray(int[] nums) {
        return getMaxSubArray(nums,0,nums.length-1);
    }
    public int getMaxSubArray(int[] nums,int left,int right){
        if (left==right){
            return nums[left];
        }else{
            int mid=(left+right)/2;
            int leftMax=getMaxSubArray(nums,left,mid);//求左边子序列的最大值(情况1)
            int rightMax=getMaxSubArray(nums,mid+1,right);//求右边子序列的最大值(情况2)
            int midMax=midSumMax(left,mid,right,nums);//求跨越终点子序列最大值(情况3)
            //逐一进行比较,返回最大值
            if (leftMax>=rightMax&&leftMax>=midMax){
                return leftMax;
            }else if (rightMax>=leftMax&&rightMax>=midMax){
                return  rightMax;
            }else{
                return  midMax;
            }
        }
    } 
    //跨越中点子序列求解
     public int midSumMax(int left,int mid,int right,int[] nums){
        int Sum=0;
        int leftMaxSum=Integer.MIN_VALUE;
       //mid为定点,左边最大子序和
        for (int i=mid;i>=left;i--){
            Sum+=nums[i];
            leftMaxSum= Math.max(leftMaxSum,Sum);
        }
        Sum=0;
        //mid+1为顶点,右边最大子序和
        int rightMaxSum=Integer.MIN_VALUE;
        for (int i=mid+1;i<=right;i++){
            Sum+=nums[i];
            rightMaxSum=Math.max(rightMaxSum,Sum);
        }
        return leftMaxSum+rightMaxSum;
    }   
}

11. 分隔字符串使同种字符出现在一起

763. 划分字母区间

class Solution {
    public List<Integer> partitionLabels(String S) {
        int[] last = new int[26];
        for (int i = 0; i < S.length(); ++i)
            last[S.charAt(i) - 'a'] = i;
        
        int j = 0, anchor = 0;
        List<Integer> ans = new ArrayList();
        for (int i = 0; i < S.length(); ++i) {
            j = Math.max(j, last[S.charAt(i) - 'a']);
            if (i == j) {
                ans.add(i - anchor + 1);
                anchor = i + 1;
            }
        }
        return ans;
    }
}

 

 

posted @ 2020-09-10 22:24  kopoo  阅读(139)  评论(0)    收藏  举报