LeetCode.1713.得到子序列的最少操作数

0 前言

刷算法第四天打卡,困难题。通过查阅资料最终解答完的感受是:分析问题,联系所学基础知识与题中细节的匹配去转化问题是解决问题的核心。
在这里插入图片描述

一、理论思路

1、HashMap

该题的target数组元素映射的下标顺序是在arr数组中寻找最大公共序列的顺序标准,所以我们用HashMap去存target数组中的元素以及下标作为<key,value>。

Map<Integer, Integer> m = new HashMap<>();
        for (int i = 0; i < target.length; i++) {
            m.put(target[i], i);
        }

2、寻找最大公共序列

1. ArrayList

通过匹配arr中的元素去寻找最大公共数组,所以我们申请一个ArrayList作为最大公共序列的接收。

List<Integer> results = new ArrayList<>();

2. 替代与添加

在匹配arr数组元素的时候,会匹配出很多子序列,而我们只需要最大那个子序列。通过ArrayList元素的添加来获取子序列的大小;通过ArrayList元素的替代来寻找其它子序列。而最终ArrayList被撑到多大,就是最大公共子序列的长度。即size()。

for (int val : arr) {
            if (m.containsKey(val)) {
                int index = m.get(val);
                int positon = binarySearch(index, results);
                //这里根据二分查找回来的值来选择是替换还是添加
                if (positon == results.size()) {
                    results.add(index);
                } else{
                    results.set(positon, index);}
            }
        }

3. return

此时我们得到了撑到最大子序列的size(),可以在arr的任意位置插入。所以返回的插入大小为target.length-size()。

return target.length - results.size();

4. 快速查找有序表–二分法

public static int binarySearch(int index, List<Integer> results) {
        //1. 当results没值时或者index>results.get(results.size()-1)时则表示这个元素可以直接加在results后面
        if (results.size() == 0 || index > results.get(results.size() - 1))
            return results.size();
        //2. 当上面的情况不成立时,则表示这个元素虽然在arr数组中,但是它不能加在results序列的后面,因为它不是比前面的元素都大
        //2. 此时的results的size认为是当前最大size,需要去得到新的results,此时用小的index去替代results中的元素
        //2. 直到把results的size撑到更大,或者根本就撑不大,然后又有更大的元素了。
        int low = 0, high = results.size() - 1;
        //有序表就可以二分查找,这样很快
        while (low < high) {
            int mid = (low + high) / 2;//为什么要+1?这里取整时默认取下,而+1操作相当于取上,这与下面判断大小的方向有关,没写好会陷入死循环
            if (index > results.get(mid)) {//存在一个问题是,把1插到0和4之间,到底该返回那个位置呐?我们应该往后撑起来,所以应该返回4这个位置
                low = mid + 1;
            } else
                high = mid;
        }
        return low;
    }

5. 算法评估

  1. 时间复杂度:O(n+mlogm),O(n):HashMap存值处;O(mlogm)遍历加二分查找。
  2. 空间复杂度:O(n+m),O(n):HashMap存值处;O(m)ArrayList获取最大公共子序列处。

二、完整源代码

public static int minOperations(int[] target, int[] arr) {
        //1. 公共序列要按target数组排序,用HashMap存target元素以及对应的下标
        Map<Integer, Integer> m = new HashMap<>();
        for (int i = 0; i < target.length; i++) {
            m.put(target[i], i);
        }
        //2. 通过这个这个HashMap存的下标序列去匹配arr,然后得出一个最大序列用ArrayList存起来
        List<Integer> results = new ArrayList<>();
        for (int val : arr) {
            if (m.containsKey(val)) {
                int index = m.get(val);
                int positon = binarySearch(index, results);
                //这里根据二分查找回来的值来选择是替换还是添加
                if (positon == results.size()) {
                    results.add(index);
                } else{
                    results.set(positon, index);}
            }
        }
        //3. 返回的插入数值就是target的长度减去这个公共最大序列的size
        return target.length - results.size();
    }

    public static int binarySearch(int index, List<Integer> results) {
        //1. 当results没值时或者index>results.get(results.size()-1)时则表示这个元素可以直接加在results后面
        if (results.size() == 0 || index > results.get(results.size() - 1))
            return results.size();
        //2. 当上面的情况不成立时,则表示这个元素虽然在arr数组中,但是它不能加在results序列的后面,因为它不是比前面的元素都大
        //2. 此时的results的size认为是当前最大size,需要去得到新的results,此时用小的index去替代results中的元素
        //2. 直到把results的size撑到更大,或者根本就撑不大,然后又有更大的元素了。
        int low = 0, high = results.size() - 1;
        //有序表就可以二分查找,这样很快
        while (low < high) {
            int mid = (low + high) / 2;//为什么要+1?这里取整时默认取下,而+1操作相当于取上,这与下面判断大小的方向有关,没写好会陷入死循环
            if (index > results.get(mid)) {//存在一个问题是,把1插到0和4之间,到底该返回那个位置呐?我们应该往后撑起来,所以应该返回4这个位置
                low = mid + 1;
            } else
                high = mid;
        }
        return low;
    }

1、注意事项

注释把思想写的很清楚,而且二分法还有两处小细节,我也标注出来了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值