day61-day62【代码随想录】二刷数组

本文介绍了使用二分法解决编程问题的几个例子,包括计算有效三角形的数量、实现Pow(x,n)函数、确定在D天内送达包裹的能力以及制作m束花所需的最少天数。这些题目涉及排序、递归和迭代,展示了二分法在优化算法效率中的应用。
摘要由CSDN通过智能技术生成


前言

1、有效三角形的个数
2、Pow(x, n)
3、在 D 天内送达包裹的能力
4、制作 m 束花所需的最少天数


一、有效三角形的个数【二分法】

给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。
在这里插入图片描述
分析:
判断三条边能组成三角形的条件为:
任意两边之和大于第三边,任意两边之差小于第三边。
三条边长从小到大为 a、b、c,当且仅当 a + b > c 这三条边能组成三角形。
1、首先对数组排序
2、固定最短的两条边,二分查找最后一个小于两边之和的位置。可以求得固定两条边长之和满足条件的结果。枚举结束后,总和就是答案。

class Solution {
    public int triangleNumber(int[] nums) {
        //首先对数组进行排序:
        Arrays.sort(nums);
        int n = nums.length;
        int res =0;
        for(int i=0;i<n-2;i++){  //选第一条边
            for(int j=i+1;j<nums.length-1;j++){ //选第二条边
                int s = nums[i]+nums[j];//最小的两边相加
                //二分法找第三边 目的:找到最后一次出现比s小的位置
                int l=j+1;
                int r = n-1;
                while(l<=r){
                    int mid = (l+r)/2;
                    if(nums[mid]<s){//还要继续往前找
                        l=mid+1;
                    }else{
                        r=mid-1;
                    }
                }
                if(nums[r]<s){
                    res +=r-j;
                }
            }
        }
        return res;
    }
}

二、Pow(x, n)(力扣50)

实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,xn )

在这里插入图片描述

方法一

快速幂 + 递归
在这里插入图片描述在这里插入图片描述

class Solution {
    public double myPow(double x, int n) {
        long N = n;
        return N>=0? quickMul(x,N):1.0/quickMul(x,-N);
    }
    public double quickMul(double x,long N){
        if(N==0) return 1.0;
        double y = quickMul(x,N/2);
        return N%2==0? y*y:y*y*x;
    }
}

在这里插入图片描述

方法二

快速幂 + 迭代
递归需要使用额外的栈空间,试着将递归转写为迭代
在这里插入图片描述

class Solution {
    public double myPow(double x, int n) {
        long N = n;
        return N>=0? quickMul(x,N):1.0/quickMul(x,-N);
    }
    public double quickMul(double x,long N){
        double ans = 1.0;
        double x_contribute = x;
        //对N进行二进制拆分
        while(N>0){
            if(N%2==1){
                ans *= x_contribute;
            }
            x_contribute*=x_contribute;
            N = N/2;
        }
        return ans;
    }
}

在这里插入图片描述

三、在 D 天内送达包裹的能力(力扣1011)【二分法】

传送带上的包裹必须在 days 天内从一个港口运送到另一个港口。

传送带上的第 i 个包裹的重量为 weights[i]。每一天,我们都会按给出重量(weights)的顺序往传送带上装载包裹。我们装载的重量不会超过船的最大运载重量。

返回能在 days 天内将传送带上的所有包裹送达的船的最低运载能力

分析:
比较类似于爱吃香蕉的珂珂,尽可能把时间撑满
对于这道题:假设每天最低运载能力为x,然后去遍历数组计算需要几天,如果发现大于days,那么最低运载能力++,直到==days,那么这个单调递增的过程可以通过二分法进行优化.
该题的关键点在于 :由于物品不存在拆分的情况,所以最低运载能力不能小于这堆重量的最大值

class Solution {
    public int shipWithinDays(int[] weights, int days) {
        int left = Arrays.stream(weights).max().getAsInt();
        int right = Arrays.stream(weights).sum();

        while(left<right){
            int mid = (left+right)/2; //最低承载量
            int day = needDay(weights,mid);
            if(day>days){ //说明最低承载量太小了 往上加一加
                left = mid+1;
            }
            if(day<=days){
                right=mid;
            }
        }
        return right;
    }

    public int needDay(int[] weights,int weight){
        int sum=0;
        int day = 0;
        for(int i=0;i<weights.length;i++){
            sum+=weights[i];
            if(sum>weight){
                day++;
                sum=0;
                i--;
            }
            if(i==weights.length-1 && sum<=weight){
                day++;
            }
        }
        return day;
    }
}

四、制作 m 束花所需的最少天数(力扣1482)【二分法】

给你一个整数数组 bloomDay,以及两个整数 m 和 k 。

现需要制作 m 束花。制作花束时,需要使用花园中 相邻的 k 朵花 。

花园中有 n 朵花,第 i 朵花会在 bloomDay[i] 时盛开,恰好 可以用于 一束 花中。

请你返回从花园中摘 m 束花需要等待的最少的天数。如果不能摘到 m 束花则返回 -1 。
在这里插入图片描述
分析:
二分法思想:先确定边界,以及什么是需要二分的
假设「所需的最少天数」为 ans ,那么以 ans 为分割点的数轴具有「二段性」:

  • 天数范围落在 [0,ans) 无法制作完成
  • 天数范围在 [ans,+∞)可以制作完成

范围的左边界为 0(代表尚未有花绽放),范围的右边界max(bloomDay[i])【最后一朵花的开放时间,代表所有花都开完】

class Solution {

        // 二分,O(n * log(max-min)) = O(10^5 * log10^9)
    public static int minDays(int[] bloomDay, int m, int k) {
        int n = bloomDay.length;
        if (m * k > n) return -1;

        int min = bloomDay[0], max = bloomDay[0]; // 最小值,最大值
        for (int day : bloomDay) {
            min = Math.min(min, day);
            max = Math.max(max, day);
        }
        // 二分:
        int l = min, r = max, ans = max;
        while (l <= r) {
            int day = l + (r-l)/2; // 能否在day天内制作m束花?
            if (canDo(bloomDay, m, k, day)) {
                ans = day;
                r = day-1;
            } else {
                l = day+1;
            }
        }

        return ans;
    }

    // 能否在day天内制作m束花,连续k朵花制作一束花
    private static boolean canDo(int[] bloomDay, int m, int k, int day) {
        int count = 0; // 连续花朵数量
        for (int d : bloomDay) {
            if (d <= day) { // 此花已开
                count++;
                if (count == k) { // 制作1束花
                    count = 0;
                    m--;
                    if (m == 0) return true;
                }
            } else {
                count = 0;
            }
        }
        return false;
    }
}

在这里插入图片描述

class Solution {

        // 二分,O(n * log(max-min)) = O(10^5 * log10^9)
    public int minDays(int[] bloomDay, int m, int k) {
        int n = bloomDay.length;
        if (m > n/k) return -1;

        int low = Arrays.stream(bloomDay).min().getAsInt();
        int high = Arrays.stream(bloomDay).max().getAsInt();

        while (low <high) {
            int day = low + (high-low)/2; // 能否在day天内制作m束花?
            if (canDo(bloomDay, m, k, day)) {
                high = day;
            } else low = day+1;
        }
        return low;
    }

    // 能否在day天内制作m束花,连续k朵花制作一束花
    public  boolean canDo(int[] bloomDay, int m, int k, int days) {
        int makeFlowers = 0;
        int flowers = 0; // 连续花朵数量
        for(int i=0;i<bloomDay.length;i++){
            if(bloomDay[i]<=days){
                flowers++;
                if(flowers==k){
                    makeFlowers++;
                    flowers=0;
                }
            }else flowers=0;
        }
        return makeFlowers>=m;
    }
}

思考:二分法的边界条件 +1 -1 哪个加 哪个减 以及 while 循环有没有等于号

一般可以left小于等于right,用ans 记录结果 如果mid 满足条件就 ans 等于mid ,最后return ans!!!!


每日一题:使字符串平衡的最少删除次数(力扣1653)

给你一个字符串 s ,它仅包含字符 ‘a’ 和 'b’​​​​ 。

你可以删除 s 中任意数目的字符,使得 s 平衡 。当不存在下标对 (i,j) 满足 i < j ,且 s[i] = ‘b’ 的同时 s[j]= ‘a’ ,此时认为 s 是 平衡 的。

请你返回使 s 平衡 的 最少 删除次数。

分析:
前后缀分解
在这里插入图片描述

class Solution {
   public int minimumDeletions(String s) {
        int remove = 127168;
        int delA,delB;
        for(int fengexian = 0;fengexian<=s.length();fengexian++){
            int left =0;
            int right=fengexian;
            delA = findA(s,0,fengexian-1);
            delB = findB(s,fengexian,s.length()-1);
            remove = Math.min(delA+delB,remove);
        }
        return remove;
    }
    public int findA(String s,int start,int end){
        int num=0;
        if(end<start){return num;}
        for(int i=start;i<=end;i++){
            if(s.charAt(i)=='b'){
                num++;
            }
        }
        return num;
    }

    public int findB(String s,int start,int end){
        int num=0;
        if(end<start){return 0;}
        for(int i=start;i<=end;i++){
            if(s.charAt(i)=='a'){
                num++;
            }
        }
        return num;
    }
}

在这里插入图片描述

优化:

class Solution {
    public  int minimumDeletions(String S) {
        var s = S.toCharArray();
        int del = 0;
        for (var c : s)
            del += 'b' - c; // 统计 'a' 的个数
        int ans = del;
        for (var c : s) {
            // 'a' -> -1    'b' -> 1
            del += (c - 'a') * 2 - 1;
            ans = Math.min(ans, del);
        }
        return ans;
    }
}

在这里插入图片描述

每日一题:礼物的最大价值(剑指 Offer 47)

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

在这里插入图片描述

**分析:**比较简单的动规,类似机器人到达终点那道题

class Solution {
    public int maxValue(int[][] grid) {
        //动态规划
        int m = grid.length;
        int n = grid[0].length;
        int[][] dp = new int[m][n];
        //dp[i][j]数组的含义:到i行j列的最大价值为dp[i][j]
        //初始化
        int sum = 0;
        dp[0][0] = grid[0][0];
        for(int i=0;i<m;i++){
            sum+=grid[i][0];
            dp[i][0] = sum;
        }
        int sum2= 0;
        for(int j=0;j<n;j++){
            sum2+=grid[0][j];
            dp[0][j] = sum2;
        }
        
        //递推公式
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1])+grid[i][j];
            }
        }
        return dp[m-1][n-1];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值