【算法题型总结】--2、贪心算法

零、目录

一、步骤

贪心算法一般分为如下四步:

将问题分解为若干个子问题
找出适合的贪心策略
求解每一个子问题的最优解
将局部最优解堆叠成全局最优解

二、题目

1、455. 分发饼干

 思路1:优先考虑胃口,先喂饱大胃口

import java.util.*;
class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int index = s.length - 1;
        int res = 0;
        for(int i = g.length - 1; i >= 0; i --) {
            if(index >= 0 && s[index] >= g[i]) {
                res ++;
                index --;
            }
        }
        return res;
    }
}

思路2:优先考虑饼干,小饼干先喂饱小胃口

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int start = 0;
        int count = 0;
        for (int i = 0; i < s.length && start < g.length; i++) {
            if (s[i] >= g[start]) {
                start++;
                count++;
            }
        }
        return count;
    }
}

 2、376. 摆动序列

class Solution {
    public int wiggleMaxLength(int[] nums) {
        if(nums.length <= 1) {
            return nums.length;
        }
        int cur = 0;
        int pre = 0;
        int res = 1;
        for(int i=1; i<nums.length; i++) {
            cur = nums[i] - nums[i-1];
            if((cur<0 && pre>=0) || (cur>0 && pre<=0)) {
                res ++;
                pre = cur;
            }
            
        }
        return res;
    }
}

3、最大子序和

 

 解法1:贪心

大于零比较,小于〇置〇

解法2:动态规划

dp=max(dp[i-1]+num, dp[i-1])

 4、跳跃游戏

class Solution {
    public boolean canJump(int[] nums) {
        if(nums.length <= 1) {
            return true;
        }
        int cover = nums[0];
        for(int i = 0; i <= cover; i ++) {
            cover = Math.max(cover, i + nums[i]);
            if(cover >= nums.length - 1) {
                return true;
            }
        }
        return false;
    }
}

1、股票无限次交易 NC134  

public int maxProfit (int[] prices)

 解法:逢高卖出,利润相加

import java.util.*;
 
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 计算最大收益
     * @param prices int整型一维数组 股票每一天的价格
     * @return int整型
     */
    public int maxProfit (int[] prices) {
        // write code here
        int profits = 0;
        int len = prices.length;
        for(int i =1;i<len;i++){
            if(prices[i]>prices[i-1]){
                profits+=prices[i]-prices[i-1]; //逢高卖出
            }
        }
        return profits;
    }
}

2、通配符的匹配NC44

public boolean isMatch(String s, String p)

 解法1:动态规划

dp[i][j]意思是s的前i个元素能否被p的前j个元素成功匹配。
boolean类型的dp数组,大小是[s.length+1][p.length+1],因为存在s前0个字符和p前0个字符的情况。
dp[0][0]一定是true,因为s空串和p空串是可以匹配成功的dp[1][0]~dp[s.length][0]一定都是false,因为s不为空串而p为空串是不能匹配成功的
dp[0][1]~dp[0][p.length]当s为空串的时候,而p不是空串的时候,当且仅当p的j字符以及前面都为'*'才为true。
dp[s.length][p.length]就得到了s和p最终的匹配情况。

然后填写dp数组剩余部分即可,状态转移方程:

当s[i]==p[j]或者p[j]=='?',则dp[i][j]=dp[i-1][j-1]。可以理解为当前字符成功匹配后,只需要考虑之前的字符串是否匹配即可;也可以理解为当前字符匹配成功之后,"移除"当前元素(即不需要再考虑当前元素)。
当p[j]==✳,则dp[i][j]=dp[i-1][j]||dp[i][j-1]。可以理解为当字符为✳的时候会出现两种情况,第一种是'*'需要作为一个字母与s[i]进行匹配;第二种是✳需要作为空字符(即不需要✳可以直接"移除"),所以dp[i][j-1];用逻辑或将两种情况连接,是因为只要有一种情况可以匹配成功则当前匹配成功,有点暴力算法的感觉。
最后当s[i]!=p[j]&&p[j]!=✳,dp[i][j]=false。这步可以省略,因为dp数组元素的默认值就是false,所以不必要进行显式的赋值为false。

public class Solution {
    public boolean isMatch(String s, String p) { //分别对应主串和模式串
        if(p.length() == 0) {
            if(s.length() == 0) return true;
            else return false;
        }
        boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];
        dp[0][0] = true;
        for (int i = 1; i < dp[0].length; i ++ ) { // 处理第一行,注意多个"*"的情况
            if(p.charAt(i - 1) == '*') dp[0][i] = dp[0][i - 1]; 否则为false
        }
        for (int i = 1; i < dp.length; i ++ ) {
            for (int j = 1; j < dp[0].length; j ++ ) {
                if(s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '?') dp[i][j] = dp[i - 1][j - 1];
                if(p.charAt(j - 1) == '*') dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
            }
        }
        return dp[dp.length - 1][dp[0].length - 1];
    }
}

解法2:贪心

如果i和j标记的字符正好相等或者j字符是'?'匹配成功,则"移除"i和j元素,即自增i、j。
否则如果j字符是✳(*号)依然可以匹配成功,则用istart和jstart分别标记i元素和j元素之后自增j。
否则如果istart>-1说明之前匹配过✳,因为✳可以匹配多个字符,所以这里要再次利用这个最近匹配过的✳匹配更多的字符,移动i标记istart的下一个字符,再让istart重新标记i元素同时移动j标记jstart的下一个字符。
上述三种情况都不满足,则匹配失败,返回false。
最后当s中的字符都判断完毕,则认为s为空,此时需要p为空或者p中只剩下星号的时候,才能成功匹配。

public class Solution {
    public boolean isMatch(String s, String p) {
          if (p==null||p.isEmpty())return s==null||s.isEmpty();
    int i=0,j=0,istart=-1,jstart=-1,slen=s.length(),plen=p.length();
    //判断s的所有字符是否匹配
    while (i<slen){
        //三种匹配成功情况以及匹配失败返回false
        if (j<plen&&(s.charAt(i)==p.charAt(j)||p.charAt(j)=='?')){
            i++;
            j++;
        }else if (j<plen&&p.charAt(j)=='*'){
            istart=i;
            jstart=j++;
        }else if (istart>-1){
            i=++istart;
            j=jstart+1;
        }else {
            return false;
        }
    }
    //s中的字符都判断完毕,则认为s为空,此时需要p为空或者p中只剩下星号的时候,才能成功匹配。
    while (j<plen&&p.charAt(j)=='*')j++;
    return j==plen;
    }
}

3、分糖果问题

public int candy (int[] arr)

 

 传入得分,计算需要的糖果

方法1:贪心+动态规划,从左向右遍历,从右向左遍历,第二次遍历时相加

   public int candy (int[] arr) {
        int n=arr.length;
        int[]dp=new int[n];//记录每个孩子的糖果数目
        //从左向右,若是右边得分高,那么就得到左边的糖果数+1,否则设为1
        dp[0]=1;
        for(int i=1;i<n;i++){
            if(arr[i]>arr[i-1]) dp[i]=dp[i-1]+1;
            else dp[i]=1;
        }
        //从右向左遍历,若是左边比右边得分高,那么糖果数右边+1
        int sum=dp[n-1];
        for(int i=n-2;i>=0;i--){
            if(arr[i]>arr[i+1]&&(dp[i]<=dp[i+1])){
                dp[i]=dp[i+1]+1;
            }
            sum+=dp[i];
        }
        return sum;
    }

类似方法:

public int candy (int[] arr) {
        int[] tmp= new int[arr.length];
        Arrays.fill(tmp,1);
        int count=0;
 
        for(int i=1;i<arr.length;i++){
            if(arr[i]>arr[i-1]){
                tmp[i]=tmp[i-1]+1;
            }
        }
         for(int i=arr.length-1;i>0;i--){
            if(arr[i-1]>arr[i]){
                tmp[i-1]=Math.max(tmp[i-1],tmp[i]+1);
            }
        }
        for(int i:tmp)
            count+=i;
        return count;
    }

也可对dp数组fill

4、拼接所有的字符串产生字典序最小的字符串   NC85

public String minString (String[] strs)

 方法1:工具类的sort函数

public String minString (String[] strs) {
    // write code here
    StringBuffer sb = new StringBuffer();
    Arrays.sort(strs, new Comparator<String>(){
        @Override
        public int compare (String s1, String s2) {
            return (s1+s2).compareTo(s2+s1);
        }
    });
 
    for (String s:strs) {
        sb.append(s);
    }
 
    return sb.toString();
}

方法2:堆排序,最终存的是由小到大

import java.util.*;
 
public class Solution {
    /**
     * 
     * @param strs string字符串一维数组 the strings
     * @return string字符串
     */
    public String minString (String[] strs) {
        // write code here
        if(strs==null){
            return null;
        }
        return stringsort(strs);
    }
    public class MinComparator implements Comparator<String>{
        public int compare(String o1,String o2){
            return (o1+o2).compareTo(o2+o1);
        }
    }
    public String stringsort(String[] str){
        PriorityQueue<String> minPriorityQueue=new PriorityQueue<>(new MinComparator());
        for(int i=0;i<str.length;i++){
            minPriorityQueue.add(str[i]);
        }
        StringBuilder ans=new StringBuilder();
        for(int i=0;i<str.length;i++){
            ans.append(minPriorityQueue.poll());
        }
        return ans.toString();
    }
}

方法3:使用StringBuilder

import java.util.*;
 
 
public class Solution {
    /**
     * 
     * @param strs string字符串一维数组 the strings
     * @return string字符串
     */
    // 字典序最小判断函数
    public static class MyComparator implements Comparator<String>{
        public int compare(String a,String b){
            return (a+b).compareTo(b+a);
        }
    }
     
    public String minString (String[] strs) {
        // write code here
        // 特殊情况
        if(strs==null || strs.length==0){
            return "";
        }
        // 字符串数组排序,规则 new MyComparator()
        Arrays.sort(strs,new MyComparator());
        StringBuffer res=new StringBuffer();
        // 拼接生成字符串
        for(int i=0;i<strs.length;i++){
            res.append(strs[i]);
        }
        return res.toString();
    }
}

5、主持人调度 NC147

public int minmumNumberOfHost (int n, int[][] startEnd)

 解法:优先队列

import java.util.*;
 
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 计算成功举办活动需要多少名主持人
     * @param n int整型 有n个活动
     * @param startEnd int整型二维数组 startEnd[i][0]用于表示第i个活动的开始时间,startEnd[i][1]表示第i个活动的结束时间
     * @return int整型
     */
    public int minmumNumberOfHost (int n, int[][] startEnd) {
        // write code here
        Arrays.sort(startEnd,(int[]o1,int[]o2)->{
            if(o1[0]==o2[0])return o1[1]-o2[1];
            return o1[0]-o2[0];
        });
        PriorityQueue<Integer>que=new PriorityQueue<>();
        int ans=0;
        for(int i=0;i<startEnd.length;i++){
            int start=startEnd[i][0];
            int end=startEnd[i][1];
            if(!que.isEmpty()&&que.peek()<=start){ 所有元素中小于当前元素
                que.poll();
            }else{
                ans++;
            }
            que.add(end);
        }
        return ans;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值