文章目录
- 前言
- 一、有效三角形的个数【二分法】
- 二、Pow(x, n)(力扣50)
- 方法一
- 方法二
- 三、在 D 天内送达包裹的能力(力扣1011)【二分法】
- 四、制作 m 束花所需的最少天数(力扣1482)【二分法】
- 每日一题:使字符串平衡的最少删除次数(力扣1653)
- 每日一题:礼物的最大价值(剑指 Offer 47)
前言
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];
}
}