一起学算法(贪心篇)

文章介绍了贪心算法的基本概念,即每次做出当前看起来最佳的选择,不考虑全局最优。通过几个例子展示了贪心算法在解决最大乘积差、三角形最大周长、数组拆分、救生艇分配和饼干分配等问题时的有效性,通常涉及排序和局部最优决策。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概念:

所谓贪心,总是做出在当前看来是最好的选择,也就是说,不从整体最优进行考虑,算法得到的是在某种意义上的局部最优解,比如当我们初中考试的时候,如果我们每科都考的是最优的,那么总成绩相对于而言也是最优的,这就是贪心算法的核心

部分题目分析:

1.最大乘积差
public int maxProductDifference(int[] nums) {
        if(nums==null||nums.length==0){
            return 0;
        }
         int n=nums.length;
        fastSort(nums,0,n-1);
        return (nums[n-1]*nums[n-2]-nums[0]*nums[1]);
    }
    //快排,将数组进行排序
    public void fastSort(int[] nums,int left,int right){
         if(nums==null||nums.length==0){
            return;
        }
        if(left>=right){
            return;
        }
        int i=left;
        int j=right;
        int pvoit=nums[left];
        while(i<j){
           //在右边找到第一个小的
           while(i<j&&nums[j]>pvoit){
               j--;
           }
           if(i<j){
               nums[i++]=nums[j];
           }
           //在左边找第一个大的
           while(i<j&&nums[i]<pvoit){
               i++;
           }
           if(i<j){
               nums[j--]=nums[i];
           }
        }
        nums[i]=pvoit;
        fastSort(nums,i+1,right);
        fastSort(nums,left,i-1);
    }
  • 先对数组进行排序(这里大家可以顺便温习一下之前讲的算法)
  • 利用贪心,直接取最大的两个数和最小的两个数
2.三角形的最大周长
public int largestPerimeter(int[] nums) {
       if(nums==null||nums.length==0){
           return 0;
       }
       int n=nums.length;
       fastSort(nums,0,n-1);
       for(int i=n-1;i>=2;i--){
           if(nums[i-2]+nums[i-1]>nums[i]){
               return nums[i]+nums[i-1]+nums[i-2];
           }
       }
       return 0;
    }
     //快排,将数组进行排序
    public void fastSort(int[] nums,int left,int right){
        if(nums==null||nums.length==0){
            return;
        }
        if(left>=right){
            return;
        }
        int i=left;
        int j=right;
        int pvoit=nums[left];
        while(i<j){
            //在右边找到第一个小的
            while(i<j&&nums[j]>pvoit){
                j--;
            }
            if(i<j){
                nums[i++]=nums[j];
            }
            //在左边找第一个大的
            while(i<j&&nums[i]<pvoit){
                i++;
            }
            if(i<j){
                nums[j--]=nums[i];
            }
        }
        nums[i]=pvoit;
        fastSort(nums,i+1,right);
        fastSort(nums,left,i-1);
    }
  • 利用三角形两边之和大于第三边的性质,假设小的两条边为a和b,大的那条边为c,必须满足等式a+b>c
  • 这个问题是要求最大的三角形周长,那么势必最最大的那条边越大越好,所以我们可以枚举将所有的边按照递增排序,然后逆序枚举最大的那条边c,去剩下的边里找另外的两条边,最好的情况肯定是比c小的最大和次大的最优,那么这两边都不能满足条件,剩下的边也肯定不能满足条件(递增排序)
3.数组拆分
  public int arrayPairSum(int[] nums) {
         if(nums==null||nums.length==0){
             return 0;
         }
        fastSort(nums,0,nums.length-1);
         int res=0;
        for (int i = 0; i <nums.length;i++) {
            if(i%2==0){
                res+=nums[i];
            }
        }
        return res;
    }
     //快排,将数组进行排序
    public void fastSort(int[] nums,int left,int right){
        if(nums==null||nums.length==0){
            return;
        }
        if(left>=right){
            return;
        }
        int i=left;
        int j=right;
        int pvoit=nums[left];
        while(i<j){
            //在右边找到第一个小的
            while(i<j&&nums[j]>pvoit){
                j--;
            }
            if(i<j){
                nums[i++]=nums[j];
            }
            //在左边找第一个大的
            while(i<j&&nums[i]<pvoit){
                i++;
            }
            if(i<j){
                nums[j--]=nums[i];
            }
        }
        nums[i]=pvoit;
        fastSort(nums,i+1,right);
        fastSort(nums,left,i-1);
    }
  • 对数组进行排序
  • 选择偶数项进行相加
4.救生艇
  public int numRescueBoats(int[] people, int limit) {
        int left=0;
        int right=people.length-1;
        Arrays.sort(people);
        int count=0;
        while(left<=right){
            if(left==right){
                count++;
                break;
            }else if(people[left]+people[right]>limit){
                right--;
                count++;
            }else{
                count++;
                right--;
                left++;
            }
        }
return count;
    }
  • 对数组进行排序
  • 如果只剩下一个人,那么直接加上一只船,并且跳出循环
  • 如果最重的那个人和最轻的那个人加起来不能做同一个船,那么最重的那个人势必只能一个人坐船
  • 如果最重的那个人可以和最轻的那个人一起坐船,那就顺带捎上
5.分发饼干
    //贪心算法   尽量满足胃口大的孩子
    public int findContentChildren(int[] g, int[] s) {
       //对入参进行判断
        if(g==null||g.length==0||s==null||s.length==0){
            return 0;
        }
        Arrays.sort(g);
        Arrays.sort(s);
        //先遍历胃口大的孩子  然后在遍历饼干
        int index=s.length-1;
        int count=0;
        for (int i =g.length-1; i>=0; i--) {
             if(index>=0&&s[index]>=g[i]){
                    index--;  
                     count++;
                                     
                 }
             }
        return count;
    }
  • 分别对胃口值和饼干都进行递增排序
  • 然后将两个指针指向胃口值和饼干值的末尾
  • 如果一个饼干和胃口值满足s[index]>=g[i],那么指针都往前移动一格,且答案+1
  • 否则,胃口值的指针往前一移动一格,寻找更加容易满足条件的值,直到饼干或者胃口枚举完成
6.最少操作使数组递增
    public int minOperations(int[] nums) {
         int ans=0; int pre=nums[0];
         for(int i=1;i<nums.length;i++){
            //保证当前数一定比前面那个数多1或者本身就比那个数大
            pre=Math.max(pre+1,nums[i]);
            ans+=pre-nums[i];
         }
         return ans;
    }
  • 如果当前的数比前一个数大,则没有任何的消耗,并且重置前一个数为当前数+1,进行下一轮判断
  • 否则,需要把当前的数变为前一个数+1
7.有效三角形的个数
   //输出可围成的三角形
    public int triangleNumber(int[] nums) {
     //对入参进行判断
        if(nums==null||nums.length<3){
            return 0;
        }
        Arrays.sort(nums);
        int n=nums.length;
        int count=0;
        for(int i=n-1;i>=2;i--){
            //第三条边
            int x=nums[i];
            int left=0;
            int right=i-1;
            while(left<right){
                if(nums[left]+nums[right]<=x){
                    left++;
                }else{
                    count+=right-left;
                    right--;
                }
            }
            
        }
        return count;
    }
  • 首先将数组元素进行递增排序
  • 利用for循环,不断的枚举最长的那条边
  • 利用前后指针的思想去寻找合适的解
  • 因为nums[right]+nums[left]>=x,因为数组是递增的,所以left+1~right中的数字都满足这个条件,所以在[left,right]范围内可以形成三角形的解的个数为right(包含关系)-left=count

leetcode题单:

6和9组成的最大数字

 public int maximum69Number (int num) {
        //利用栈进行操作
        Stack<Integer> stack=new Stack<>();
        while(num!=0){
            int str=num%10;
            stack.push(str);
            num/=10;
        }
        int count=0;
        int sum=0;
        while(!stack.isEmpty()){
            int num1=stack.pop();
            if(num1==6&&count==0){
                num1=9;
                count++;
            }
            sum=sum*10+num1;
        }
        return sum;
    }

子字符串的最优划分

    public int partitionString(String s) {
        int[] hash=new int[26];
        int count=0;
        
        for(char c:s.toCharArray()){
            if(hash[c-'a']==1){
                count++;
                Arrays.fill(hash,0);
            }
            hash[c-'a']++;
        }
        return count+1;
    }


     public int partitionString(String s) {
        int hash=0;
        int count=0;
        for(char c:s.toCharArray()){
            if((hash>>(c-'a')&1)==1){
                count++;
                hash=0;
            }
            hash|=1<<(c-'a');
        }
        return count+1;
    }

柠檬水找零

 //贪心问题,每次按照最大的去找
    public boolean lemonadeChange(int[] bills) {
    if(bills==null||bills.length==0){
        return true;
    }
    int five=0;
    int ten=0;
    //遍历账单
    for(int i=0;i<bills.length;i++){
        //如果收取的是5,直接收入囊中
        if(bills[i]==5){
            five++;
        //如果有5,直接找零
        }else if(bills[i]==10){
                ten++;
                five--;
        }else{
            if(ten>0){
                ten--;
                five--;
            }else{
                five-=3;
            }
        }
         if(five<0||ten<0){
            return false;
        }
    }
   
    return true;
    }

分割数组的最大值

 public int splitArray(int[] nums, int k) {
         return shipWithinDays(nums,k);
    }
       public int shipWithinDays(int[] weights, int days) {
        if(weights==null||weights.length==0){
            return 0;
        }
       //二分查找的范围
        //left是数组中的最大值
        //right是数组的全部元素之和
        int left=0;
        int right=0;
        for(int w:weights){
        left=Math.max(left,w);
        right+=w;
        }
        while(left<right){
            int mid=left+(right-left)/2;
            if(f(weights,mid)<=days){
                right=mid;
            }else{
                left=mid+1;
            }
        }
        return left;
    }
    //当运输能力为x时,需要f(x)天运输完成
    public int f(int[] weights,int x){
        int day=0;
        for (int i = 0; i <weights.length;) {
            int cap=x;
            while(i<weights.length){
                if(cap<weights[i]){
                      break;
                }else{
                 cap-=weights[i];
                i++;
                }
            }
            day++;
        }
        return day;
    }

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃橘子的Crow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值