【LeetCode: 1187. 使数组严格递增 | 暴力递归=>记忆化搜索=>动态规划 】

在这里插入图片描述

🚀 算法题 🚀

🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀
🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨
🌲 作者简介:硕风和炜,CSDN-Java领域新星创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文|经验分享|好用的网站工具分享💎💎💎
🌲 恭喜你发现一枚宝藏博主,赶快收入囊中吧🌻
🌲 人生如棋,我愿为卒,行动虽慢,可谁曾见我后退一步?🎯🎯

🚀 算法题 🚀

在这里插入图片描述

🚗 知识回顾

大家再看这道题目之前,可以先去看一下我之前写过的一篇关于最长递增子序列算法题的博客,再看这个题目就更容易理解了。
博客的地址放到这里了,可以先去学习一下这到题目。

🚩 题目链接

⛲ 题目描述

给你两个整数数组 arr1 和 arr2,返回使 arr1 严格递增所需要的最小「操作」数(可能为 0)。

每一步「操作」中,你可以分别从 arr1 和 arr2 中各选出一个索引,分别为 i 和 j,0 <= i < arr1.length 和 0 <= j < arr2.length,然后进行赋值运算 arr1[i] = arr2[j]。

如果无法让 arr1 严格递增,请返回 -1。

示例 1:

输入:arr1 = [1,5,3,6,7], arr2 = [1,3,2,4]
输出:1
解释:用 2 来替换 5,之后 arr1 = [1, 2, 3, 6, 7]。
示例 2:

输入:arr1 = [1,5,3,6,7], arr2 = [4,3,1]
输出:2
解释:用 3 来替换 5,然后用 4 来替换 3,得到 arr1 = [1, 3, 4, 6, 7]。
示例 3:

输入:arr1 = [1,5,3,6,7], arr2 = [1,6,3,3]
输出:-1
解释:无法使 arr1 严格递增。

提示:

1 <= arr1.length, arr2.length <= 2000
0 <= arr1[i], arr2[i] <= 10^9

🌟 求解思路&实现代码&运行结果


⚡ 暴力递归

🥦 求解思路
  1. 题目需要我们在arr2数组中寻找元素,使得arr1数组中的元素保持严格递增的最少次数,如果都不满足,直接返回false。
  2. 那么,该题怎么求解呢?大的问题规模我们已经分析完了,接下来我们看可不可以继续划分为更小的规模,如果前面从0到i的位置都是递增的,那问题是不是就变成从i+1到最后一个位置要保持严格递增的最小次数了呢?是的,所以我们可以通过递归来求解。
  3. 这个题目还需要我们在arr2中查找元素,我们可以使用二分进行查找,来提高我们的效率。
  4. 在arr2查找什么元素呢?因为在递归的时候,我们会维护一个之前选择的状态,每次递归的时候我们都会判断,如果当前元素值是小于我们之前位置元素的,此时我们需要进行二分查找,找到大于target最左侧的元素。
  5. 有了基本的思路,接下来就是具体的实现。
🥦 实现代码

注意:代码可以继续优化,下面的代码有一些冗余的地方还可以继续修正,这个就是一个粗略的版本。

class Solution {
    public int makeArrayIncreasing(int[] arr1, int[] arr2) {
        Arrays.sort(arr2);
        int ans=process(0,arr1,Integer.MIN_VALUE,arr2);
        return ans<Integer.MAX_VALUE/2?ans:-1;
    }

    public int process(int index,int[] arr1,int pre,int[] arr2){
        if(index>=arr1.length) return 0;
        int min=Integer.MAX_VALUE/2;
        // 必须替换
        if(arr1[index]<=pre){
            // 在arr2中找到最小的大于pre的那个数
            int moreIndex=binarySearch(arr2,pre);
            if(moreIndex<arr2.length){
                min=Math.min(min,process(index+1,arr1,arr2[moreIndex],arr2)+1);
            }
        }else{
            // 可以替换 可以不替换
            int p1=process(index+1,arr1,arr1[index],arr2);
            int moreIndex=binarySearch(arr2,pre);
            int p2=Integer.MAX_VALUE;
            if(moreIndex<arr2.length){
                p2=Math.min(min,process(index+1,arr1,arr2[moreIndex],arr2)+1);
            }
            min=Math.min(p1,p2);
        }
        return min;
    }

    public int binarySearch(int[] nums,int target){
        int left=-1,right=nums.length;
        while(left+1<right){
            int mid=(left+right)>>1;
            if(nums[mid]>target){
                right=mid;
            }else{
                left=mid;
            }
        }
        return right;
    }
}
🥦 运行结果

时间超限了,不要紧哦,我还有锦囊妙计!

在这里插入图片描述


⚡ 记忆化搜索

🥦 求解思路
  1. 根据我们递归的分析,在递归的过程中会产生重复的子过程,所以我们想到了加一个缓存表,也就是我们的记忆化搜索。
  2. 使用记忆化搜索的时候,我们可以使用Map来记录每次缓存的结果,使用Map来充当缓存的方式也有很多,比如说开辟一个Map类型的数组,数组中存储的Map<Integer,Integer>,这些方式都是可以实现的,感兴趣的同学都可以进行积极的尝试。
🥦 实现代码
class Solution {

    Map<String, Integer> map=new HashMap<>();

    public int makeArrayIncreasing(int[] arr1, int[] arr2) {
        Arrays.sort(arr2);
        int ans=process(0,arr1,Integer.MIN_VALUE,arr2);
        return ans<Integer.MAX_VALUE/2?ans:-1;
    }

    public int process(int index,int[] arr1,int pre,int[] arr2){
        if(index>=arr1.length) return 0;
        if(map.containsKey(index+"-"+pre)){
            return map.get(index+"-"+pre);
        }
        int min=Integer.MAX_VALUE/2;
        // 必须替换
        if(arr1[index]<=pre){
            // 在arr2中找到最小的大于pre的那个数
            int moreIndex=binarySearch(arr2,pre);
            if(moreIndex<arr2.length){
                min=Math.min(min,process(index+1,arr1,arr2[moreIndex],arr2)+1);
            }
        }else{
            // 可以替换 可以不替换
            int p1=process(index+1,arr1,arr1[index],arr2);
            int moreIndex=binarySearch(arr2,pre);
            int p2=Integer.MAX_VALUE;
            if(moreIndex<arr2.length){
                p2=Math.min(min,process(index+1,arr1,arr2[moreIndex],arr2)+1);
            }
            min=Math.min(p1,p2);
        }
        map.put(index+"-"+pre,min);
        return min;
    }

    public int binarySearch(int[] nums,int target){
        int left=-1,right=nums.length;
        while(left+1<right){
            int mid=(left+right)>>1;
            if(nums[mid]>target){
                right=mid;
            }else{
                left=mid;
            }
        }
        return right;
    }
}
🥦 运行结果

在这里插入图片描述


💬 共勉

最后,我想和大家分享一句一直激励我的座右铭,希望可以与大家共勉!

在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
给定一个整数数组 nums 和一个目标值 target,要求在数组中找出两个数的和等于目标值,并返回这两个数的索引。 思路1:暴力法 最简单的思路是使用两层循环遍历数组的所有组合,判断两个数的和是否等于目标值。如果等于目标值,则返回这两个数的索引。 此方法的时间复杂度为O(n^2),空间复杂度为O(1)。 思路2:哈希表 为了优化时间复杂度,可以使用哈希表来存储数组中的元素和对应的索引。遍历数组,对于每个元素nums[i],我们可以通过计算target - nums[i]的值,查找哈希表中是否存在这个差值。 如果存在,则说明找到了两个数的和等于目标值,返回它们的索引。如果不存在,将当前元素nums[i]和它的索引存入哈希表中。 此方法的时间复杂度为O(n),空间复杂度为O(n)。 思路3:双指针 如果数组已经排序,可以使用双指针的方法来求解。假设数组从小到大排序,定义左指针left指向数组的第一个元素,右指针right指向数组的最后一个元素。 如果当前两个指针指向的数的和等于目标值,则返回它们的索引。如果和小于目标值,则将左指针右移一位,使得和增大;如果和大于目标值,则将右指针左移一位,使得和减小。 继续移动指针,直到找到两个数的和等于目标值或者左指针超过了右指针。 此方法的时间复杂度为O(nlogn),空间复杂度为O(1)。 以上三种方法都可以解决问题,选择合适的方法取决于具体的应用场景和要求。如果数组规模较小并且不需要考虑额外的空间使用,则暴力法是最简单的方法。如果数组较大或者需要优化时间复杂度,则哈希表或双指针方法更合适。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

硕风和炜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值