算法专题二十:贪心算法

柠檬水找零

860. 柠檬水找零 - 力扣(LeetCode)

在找20元的时候使用了贪心算法," 尽可能的找10+5,而不是5+5+5".

java 复制代码
class Solution {
    public boolean lemonadeChange(int[] bills) {
        int ten=0;
        int five=0;
        for(int x:bills){
            if(x==5){
                five++;
            }else if(x==10){
                if(five!=0){
                    five--;
                    ten++;
                }else{
                    return false;
                }
                
            }else{
                if(five !=0 && ten!=0){
                    ten--;
                    five--;
                }else if(five>=3){
                    five-=3;
                }else{
                    return false;
                }
                
            }
        }
        return true;
    }
}

将数组和减半的最小操作次数

2208. 将数组和减半的最少操作次数 - 力扣(LeetCode)

java 复制代码
class Solution {
    public int halveArray(int[] nums) {
        PriorityQueue<Double> heap=new PriorityQueue<>((a,b)->b.compareTo(a));
        int count=0;
        double sum=0.0;
        for(int x:nums){
            heap.offer((double)x);
            sum+=x;
        }
        sum/=2.0;

        while(sum>0){
            double t=heap.poll()/2.0;
            sum-=t;
            count++;
            heap.offer(t);
        }
        return count;

    }
}

最大数

179. 最大数 - 力扣(LeetCode)

java 复制代码
class Solution {
    public String largestNumber(int[] nums) {
        int n=nums.length;
        String[] str=new String[n];
        for(int i=0;i<n;i++){
            str[i]=""+nums[i];
        }
        Arrays.sort(str,(a,b)->{
            return (b+a).compareTo(a+b);
        });
        StringBuffer s=new StringBuffer();
        for(String m:str){
            s.append(m);
        }
        
        if(s.charAt(0)=='0'){
            return "0";
        }
        return s.toString();
    }
}

摆动序列

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

left中存的值是当前节点的与前一个数的差值,right中存的是当前节点和后一个点的差值

java 复制代码
class Solution {
    public int lengthOfLIS(int[] nums) {
        int n=nums.length;
        int[] dp=new int[n];
        int ret=0;
        Arrays.fill(dp,1);
        for(int i=n-1;i>=0;i--){
            for(int j=i+1;j<n;j++){
                if(nums[i]<nums[j]){
                    dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
            ret=Math.max(ret,dp[i]);
        }
        return ret;
        
    }
}

最长的递增子序列

可以使用深搜(dfs),也可以使用动态规划,也可以用贪心算法

300. 最长递增子序列 - 力扣(LeetCode)

java 复制代码
class Solution {
    public int lengthOfLIS(int[] nums) {
        ArrayList<Integer> ret=new ArrayList();
        int n=nums.length;
        ret.add(nums[0]);
        for(int i=1;i<n;i++){
            if(ret.get(ret.size()-1)<nums[i]){
                ret.add(nums[i]);
            }else{
                int left=0;
                int right=ret.size()-1;

                while(left<right){
                    int mid=(right+left)/2;
                    if(ret.get(mid)>=nums[i]){
                        right=mid;
                    }else{
                        left=mid+1;
                    }
                }
                ret.set(left,nums[i]);
            }
        }
        return ret.size();
    }
}

递增的三元子序列

334. 递增的三元子序列 - 力扣(LeetCode)

java 复制代码
class Solution {
    public boolean increasingTriplet(int[] nums) {
        int a=nums[0];
        int b=Integer.MAX_VALUE;
        int n=nums.length;
        for(int i=1;i<n;i++){
            if(nums[i]>b){
                return true;
            }else if(nums[i]>a){
                b=nums[i];
            }else{
                a=nums[i];
            }
        }
        return false;
    }
}

使用ArrayList+二分查找

java 复制代码
class Solution {
    public boolean increasingTriplet(int[] nums) {
        ArrayList<Integer> ret=new ArrayList<>();
        ret.add(nums[0]);
        int n=nums.length;
        for(int i=1;i<n;i++){
            if(ret.get(ret.size()-1)<nums[i]){
                ret.add(nums[i]);
            }else{
                int left=0;
                int right=ret.size()-1;
                while(left<right){
                    int mid=(left+right)/2;
                    if(ret.get(mid)>=nums[i]){
                        right=mid;
                    }else{
                        left=mid+1;
                    }
                }
                ret.set(left,nums[i]);
            }
            if(ret.size()>=3){
                return true;
            }
        }
        return false;
    }
}

最长的连续递增数列

674. 最长连续递增序列 - 力扣(LeetCode)

java 复制代码
class Solution {
    public int findLengthOfLCIS(int[] nums) {
        int n=nums.length;
        int ret=0;
        for(int i=0;i<n;){
            int j=i+1;
            while(j<n && nums[j]>nums[j-1] ){
                j++;
            }
            ret=Math.max(ret,j-i);
            i=j;
        }
        return ret;

    }
}

买卖股票的最佳时期

121. 买卖股票的最佳时机 - 力扣(LeetCode)

暴力解法->两层for直接暴力枚举

java 复制代码
class Solution {
    public int maxProfit(int[] prices) {
        int n=prices.length;
        int result=0;
        for(int i=0;i<n;i++){
            int j=i+1;
            int ret=0;
            while(j<n && prices[j]>prices[i]){
                
                ret=Math.max(ret,prices[j]);
                j++;
            }
            result=Math.max(result,ret-prices[i]);
        }
        return result;
    }
}

贪心->优化暴力解法

java 复制代码
class Solution {
    public int maxProfit(int[] prices) {
        int n=prices.length;
        int prevMin=Integer.MAX_VALUE;
        int ret=0;
        for(int i=0;i<n;i++){
            ret=Math.max(ret,prices[i]-prevMin);
            prevMin=Math.min(prevMin,prices[i]);
        }
        return ret;
    }
}

买卖股票的最佳时期Ⅱ

122. 买卖股票的最佳时机 II - 力扣(LeetCode)

使用双指针的方法

java 复制代码
class Solution {
    // 双指针的方法
    public int maxProfit(int[] prices) {
        int n=prices.length;
        int ret=0;
        for(int i=0;i<n;i++){
            int j=i;
            while(j+1<n && prices[j+1]>prices[j]){
                j++;
            }
            ret+=prices[j]-prices[i];
            i=j;
        }
        return ret;
    }
}

按照一天一天的方式买

java 复制代码
class Solution {
    public int maxProfit(int[] prices) {
        int n=prices.length;
        int ret=0;
        for(int i=1;i<n;i++){
            if(prices[i]>prices[i-1]){
                ret+=prices[i]-prices[i-1];
            }
        }
        return ret;
    }
}

k次取反后最大值的数组和

1005. K 次取反后最大化的数组和 - 力扣(LeetCode)

java 复制代码
class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        int m=0;
        int minValue=Integer.MAX_VALUE;
        int n=nums.length;
        int ret=0;
        for(int x:nums){
            if(x<0){
                m++;
            }
            minValue=Math.min(minValue,Math.abs(x));
        }

        if(m>k){
            Arrays.sort(nums);
            for(int i=0;i<k;i++){
                ret+= -nums[i];
            }
            for(int i=k;i<n;i++){
                ret += nums[i];
            }
        }
        if(m==k){
            for(int x:nums){
                ret+=Math.abs(x);
            }
        }
        if(m<k){
            if((k-m)%2==0){
                for(int x:nums){
                    ret+=Math.abs(x);
                }
            }else{
                for(int x:nums){
                    ret+=Math.abs(x);
                }
                ret-=minValue*2;
            }
        }
        return ret;
    }
    
}

按身高排序

2418. 按身高排序 - 力扣(LeetCode)

解法一:创建二元组

java 复制代码
class Solution {
    public String[] sortPeople(String[] names, int[] heights) {
        int n=heights.length;
        String[][] people=new String[n][2];
        for(int i=0;i<n;i++){
            people[i][0]=names[i];
            people[i][1]=String.valueOf(heights[i]);
        }
        Arrays.sort(people,(a,b)->{
            return Integer.parseInt(b[1])-Integer.parseInt(a[1]);
        });
        String[] ret=new String[n];
        for(int i=0;i<n;i++){
            ret[i]=people[i][0];
        }
        return ret;
    }
}

解法二:Hash表的映射

java 复制代码
class Solution {
    public String[] sortPeople(String[] names, int[] heights) {
        int n=heights.length;
        Map<Integer,String> hash=new HashMap<>();
        for(int i=0;i<n;i++){
            hash.put(heights[i],names[i]);
        }
        Arrays.sort(heights);
        String[] ret=new String[n];
        for(int i=0;i<n;i++){
            ret[i]=hash.get(heights[n-1-i]);
        }
        return ret;
    }
}

解法三:把下标进行排序

java 复制代码
class Solution {
    public String[] sortPeople(String[] names, int[] heights) {
        int n=heights.length;
        Integer[] index=new Integer[n];
        for(int i=0;i<n;i++){
            index[i]=i;
        }
        Arrays.sort(index,(a,b)->{
            return heights[b]-heights[a];
        });
        String[] ret=new String[n];

        for(int i=0;i<n;i++){
            ret[i]=names[index[i]];
        }
        return ret;
    }
}

优势洗牌(田忌赛马)

870. 优势洗牌 - 力扣(LeetCode)

java 复制代码
class Solution {
    public int[] advantageCount(int[] nums1, int[] nums2) {
        int n=nums2.length;
        Integer[] index=new Integer[n];
        Arrays.sort(nums1);
        for(int i=0;i<n;i++){
            index[i]=i;
        }
        Arrays.sort(index,(a,b)->{
            return nums2[a]-nums2[b];
        });
        int left=0;
        int right=n-1;
        int[] ret=new int[n];
        for(int x:nums1){
            if(x>nums2[index[left]]){
                ret[index[left]]=x;
                left++;
            }else{
                ret[index[right]]=x;
                right--;
            }
        }
        return ret;
    }
}

最长回文串

409. 最长回文串 - 力扣(LeetCode)

java 复制代码
class Solution {
    public int longestPalindrome(String s) {
        int[] hash=new int[127];
        for(int i=0;i<s.length();i++){
            hash[s.charAt(i)]++;
        }
        int ret=0;
        for(int x:hash){
            if(x%2==0){
                ret+=x;
            }else{
                ret+=(x/2*2);
            }
        }
        return ret<s.length()?ret+1:ret;

    }
}

增减字符串匹配

942. 增减字符串匹配 - 力扣(LeetCode)

当出现字符 I 放入最小值,当出现字符 D 放入最大值

java 复制代码
class Solution {
    public int[] diStringMatch(String s) {
        int n=s.length();
        int[] ret=new int[n+1];
        int max=n;
        int min=0;

        for(int i=0;i<n;i++){
            if(s.charAt(i)=='I'){
                ret[i]=min;
                min++;
            }
            if(s.charAt(i)=='D'){
                ret[i]=max;
                max--;
            }
        }
        ret[n]=min;
        return ret;
    }
}

分发饼干

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

先将孩子和饼干升序

贪心策略:小孩子给小饼干

java 复制代码
class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int n=g.length;
        int m=s.length;
        int ret=0;
        int i=0;
        int j=0;
        while(i<n && j<m){
            if(g[i]<=s[j]){
                ret++;
                i++;
            }
            j++;
        }
        
        return ret;
    }
}

双指针的写法:

java 复制代码
class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int n=g.length;
        int m=s.length;
        int ret=0;
        // 双指针的写法
        for(int i=0,j=0;i<n && j<m;i++,j++){
            while(j<m && g[i]>s[j]){
                j++;
            }
            if(j<m){
                ret++;
            }
        }
        
        return ret;
    }
}

最优除法

553. 最优除法 - 力扣(LeetCode)

java 复制代码
class Solution {
    public String optimalDivision(int[] nums) {
        StringBuffer ret=new StringBuffer();
        int n=nums.length;
        
        
        if(n==1){
            return ret.append(nums[0]).toString();
        }
        if(n==2){
            return ret.append(nums[0]).append("/").append(nums[1]).toString();
        }
        if(n>=3){
            ret.append(nums[0]).append("/(").append(nums[1]);
            for(int i=2;i<n;i++){
                ret.append("/").append(nums[i]);
            }    
            ret.append(")");
        }
        return ret.toString();


    }
}

跳跃游戏Ⅱ

45. 跳跃游戏 II - 力扣(LeetCode)

java 复制代码
class Solution {
    public int jump(int[] nums) {
        int left=0;
        int right=0;
        int n=nums.length;
        int maxpos=0;
        int ret=0;
        while(left<=right){
            if(maxpos>=n-1){
                return ret;
            }
            for(int i=left;i<=right;i++){
                maxpos=Math.max(maxpos,nums[i]+i);
            }
            left=right+1;
            right=maxpos;
            ret++;
        }
        return ret;
    }
}

跳跃游戏

55. 跳跃游戏 - 力扣(LeetCode)

java 复制代码
class Solution {
    public boolean canJump(int[] nums) {
        int left=0;
        int right=0;
        int n=nums.length;
        int maxPos=0;
        while(left<=right){
            if(maxPos>=n-1){
                return true;
            }
            for(int i=left;i<=right;i++){
                maxPos=Math.max(maxPos,nums[i]+i);

            }
            left=right+1;
            right=maxPos;
        }
        return false;
    }
}

加油站

134. 加油站 - 力扣(LeetCode)

java 复制代码
class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int n=gas.length;
        for(int i=0;i<n;i++){
            int rest=0;
            int step=0;
            for(;step<n;step++){
                int index=(i+step)%n;
                rest=rest+gas[index]-cost[index];
                if(rest<0){
                break;
                }
            }
            if(rest>=0){
                return i;
            }
            // 进行优化
            i+=step;
            
        }
        return -1;

    }
}

单调递增的数字

738. 单调递增的数字 - 力扣(LeetCode)

首先我们使用会超出时间限制的两种判断递增的方法

1.法一:

java 复制代码
class Solution {
    public int monotoneIncreasingDigits(int n) {
        int prev=0;
        int cur=0;
        for(int i=n;i>0;i--){
            if(judge(i))
            return i;
        }
        return -1;
    }
    public boolean judge(int n){
        int prev=0;

        while(n>0){
            prev=n%10;
            int cur=n/10%10;
            if(prev<cur){
                return false;
            }
            prev=cur;
            n/=10;
        }
        return true;
    }
}

2.法二:

java 复制代码
class Solution {
    public int monotoneIncreasingDigits(int n) {
        int prev=0;
        int cur=0;
        for(int i=n;i>0;i--){
            if(judge(i))
            return i;
        }
        return -1;
    }
    public boolean judge(int n){
        char[] ch=String.valueOf(n).toCharArray();
        int m=ch.length;
        for(int i=m-2;i>=0;i--){
            if(ch[i]>ch[i+1]){
                return false;
            }
        }
        return true;
    }
}

正确的解法:贪心(找规律)

java 复制代码
class Solution {
    public int monotoneIncreasingDigits(int n) {
        char[] s=String.valueOf(n).toCharArray();
        int i=0;
        int m=s.length;
        while(i+1<m && s[i]<=s[i+1]){
            i++;
        }
        if(i==m-1){
            return n;
        }
        while(i-1>=0 && s[i]==s[i-1]){
            i--;
        }
        s[i]--;
        for(int j=i+1;j<m;j++){
            s[j]='9';
        }
        return Integer.parseInt(new String(s));

    }
}

坏了的计算器

991. 坏了的计算器 - 力扣(LeetCode)

反着来->(正难则反)

当target>start

target为偶数时 target/=2

为奇数时 target+=1

target<start

只能 target+=1

java 复制代码
class Solution {
    public int brokenCalc(int start, int target) {
        int ret=0;
        while(target!=start){
            if(target>start){
                if(target%2==0){
                target/=2;
                ret++;
            }else{
                target+=1;
                ret++;
            }
            }else if(target<start){
                target+=1;
                ret++;
            }
        }
        return ret;
    }
}

合并区间

56. 合并区间 - 力扣(LeetCode)

java 复制代码
class Solution {
    public int[][] merge(int[][] intervals) {
        Arrays.sort(intervals,(a,b)->{
            return a[0]-b[0];
        });
        int left=intervals[0][0];
        int right=intervals[0][1];

        int n=intervals.length;
        List<int[]> ret=new ArrayList<>();
        for(int i=1;i<n;i++){
            int a=intervals[i][0];
            int b=intervals[i][1];
            //可以合并
            if(right>=a){
                right=Math.max(right,b);
            }else{//不能合并
            ret.add(new int[]{left,right});
            left=a;
            right=b;
            }
        }
        ret.add(new int[]{left,right});
        return ret.toArray(new int[0][]);
    }
}

无重叠区域

435. 无重叠区间 - 力扣(LeetCode)

java 复制代码
class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        Arrays.sort(intervals,(a,b)->{
            return a[0]-b[0];
        });
        int ret=0;
        int left=intervals[0][0];
        int right=intervals[0][1];
        for(int i=1;i<intervals.length;i++){
            if(intervals[i][0]>=right){
                // 无重叠区域,坐标相交不代表重叠
                right=intervals[i][1];
            }else{
                right=Math.min(right,intervals[i][1]);
                ret++;
            }
        }
        return ret;
    }
}

用最少数量的箭引爆气球

452. 用最少数量的箭引爆气球 - 力扣(LeetCode)

java 复制代码
class Solution {
    public int findMinArrowShots(int[][] points) {
        Arrays.sort(points,(a,b)->{
            return a[0]>b[0]?1:-1;
        });
        int ret=1;
        int left=points[0][0];
        int right=points[0][1];

        for(int i=1;i<points.length;i++){
            if(right>=points[i][0]){
                // 有重叠
                right=Math.min(right,points[i][1]);
            }else{
                ret++;
                right=points[i][1];
            }
        }
        return ret;

    }
}

整数替换

397. 整数替换 - 力扣(LeetCode)

直接用递归

java 复制代码
class Solution {

    public int integerReplacement(int n) {
        return dfs(n*1L);
    }
    public int dfs(long n){
        if(n==1){
            return 0;
        }
        if(n%2==0){
            return 1+dfs(n/2);
        }else{
            return 1+Math.min(dfs(n+1),dfs(n-1));
        }
    }
}

优化成递归+记忆化搜索

java 复制代码
class Solution {
    Map<Long,Integer> ret;
    public int integerReplacement(int n) {
        ret=new HashMap<>();
        return dfs(n*1L);
    }
    public int dfs(long n){
        if(ret.containsKey(n)){
            return ret.get(n);
        }
        if(n==1){
            ret.put(n,0);
            return ret.get(n);
        }
        if(n%2==0){
            ret.put(n,1+dfs(n/2));
            return ret.get(n);
        }else{
            ret.put(n,1+Math.min(dfs(n+1),dfs(n-1)));
            return ret.get(n);
        }
    }
}

俄罗斯套娃信封问题

354. 俄罗斯套娃信封问题 - 力扣(LeetCode)

使用动态规划的方法会超出时间的限制

java 复制代码
class Solution {
    public int maxEnvelopes(int[][] envelopes) {
        Arrays.sort(envelopes,(a,b)->{
            return a[0]-b[0];
        });
        int n=envelopes.length;
        int[] dp=new int[n];
        int ret=1;
        for(int i=0;i<n;i++){
            dp[i]=1;
            for(int j=0;j<i;j++){
                if(envelopes[i][0]>envelopes[j][0] && envelopes[i][1]>envelopes[j][1]){
                    dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
            ret=Math.max(ret,dp[i]);
        }
        return ret;
    }
}

重写排序+贪心+二分(⭐)

java 复制代码
class Solution {
    public int maxEnvelopes(int[][] e) {
         
        Arrays.sort(e, (v1, v2) -> {
            return v1[0] != v2[0] ? v1[0] - v2[0] : v2[1] - v1[1];
        });
        ArrayList<Integer> ret = new ArrayList<>();
        ret.add(e[0][1]);
        for(int i = 1; i < e.length; i++){
            int b = e[i][1];
            if(b > ret.get(ret.size() - 1)){
                ret.add(b);
            }
            else {
                int left = 0, right = ret.size() - 1;
                while(left < right)
                {
                    int mid = (left + right) / 2;
                    if(ret.get(mid) >= b) right = mid;
                    else left = mid + 1;
                }
                ret.set(left, b);
            }
        }
        return ret.size();        
    }
}
相关推荐
xlq223222 小时前
15.list(上)
数据结构·c++·list
我不会插花弄玉3 小时前
排序【由浅入深-数据结构】
c语言·数据结构
ANYOLY3 小时前
Sentinel 限流算法详解
算法·sentinel
XH华4 小时前
数据结构第三章:单链表的学习
数据结构
No0d1es4 小时前
电子学会青少年软件编程(C/C++)六级等级考试真题试卷(2025年9月)
c语言·c++·算法·青少年编程·图形化编程·六级
AndrewHZ4 小时前
【图像处理基石】图像去雾算法入门(2025年版)
图像处理·人工智能·python·算法·transformer·cv·图像去雾
Knox_Lai4 小时前
数据结构与算法学习(0)-常见数据结构和算法
c语言·数据结构·学习·算法
橘颂TA4 小时前
【剑斩OFFER】算法的暴力美学——矩阵区域和
算法·c/c++·结构与算法
不会c嘎嘎4 小时前
每日一练 -- day1
c++·算法
hansang_IR5 小时前
【记录】网络流最小割建模三题
c++·算法·网络流·最小割