每日一题算法:国庆算法补课

本文是作者国庆期间每日一题的算法学习总结,涉及动态规划和树形问题的解题思路。文章涵盖宝石与石头、两数之和、链表相加等经典问题,通过实例深入讲解动态规划的解题步骤和状态转移方程,以及利用动态规划解决树中距离之和问题。此外,还包括使用哈希表解决两数之和等问题的技巧。作者强调持续学习算法对提升编程思维和专业技能的重要性。
摘要由CSDN通过智能技术生成

10月1日 秋叶收藏集

在这里插入图片描述

class Solution {
   
    public int minimumOperations(String leaves) {
   

    }
}

解题思路:

这道题中的最少调整次数是如何得到的?我认为这道题的核心就在于这一点,那么如何得到这个最少调整次数呢?我想了很久也只能想到一个非常不可靠的暴力破解的方法。所以选择去看答案学习如何实现。

动态规划学习:

动态规划之前总是觉得自己会用,但是实际写又写不出,所以趁着这道题的机会好好学习学习什么叫动态规划,以及动态规划的题该怎么解。

动态规划解题步骤1:思考联系

能够用动态规划解决的问题必定有一个共同点,那就是可以通过之前得到的答案来推导出当前问题的答案,这个思想和递归有一些相似的地方但又不完全一样。

从思想上我们可以这样比喻这个问题。

用最经典的繁殖问题来比喻,一开始有两只兔子,每只兔子会在一个月后生下一只兔子,并且在两个月后死亡。也就是斐波那契数列。

递归就是这样一个思路,求某一个月的兔子数量,直接使用嵌套的函数,根据前一个月的兔子数推导出当前月的兔子数,如果前一个月的兔子数不可知,那么就再往前推,直到找到第一个月的兔子个数为止。

而动态规划不同,动态规划的思想是,我使用一个数组记录下每一月的兔子个数,当我要求一个月的兔子个数时,我先从第一月开始,求第一月的兔子数,然后计算第二月,从第一月得到的结果推导出第二月的结果。依次类推,直到求出第n月的兔子个数。

这样说可能还是不是很清晰,所以我以这道例题为例,尝试思考联系。

我们要解决的问题是求需要调整的叶子个数,所以我们一步到位,直接求到第n片叶子时需要调整的叶子个数。

我们把求第0-n个叶子需要调整的个数设为一个函数 f(n),但是我们发现,无法写出函数和之前一个状态的关系,因为还缺少一个状态信息,加上这个状态信息,我们就能得到函数和之前得到结果的关系了。

f(n,i)

其中n表示当前得到的结果是0-n位置上需要调整的叶子个数。i表示,当前第n个位置是在红黄红的哪一个部分。

所有的问题在设计完这个函数的同时就已经解决了。难点就在于如何找到设计这个动态规划的函数,这需要大量的练习来积攒经验。

下面就来解释这个函数之间的联系。

f(n,0)表示从第0个到第n个叶子,第n个叶子是在红黄红的第一部分时,需要替换的叶子个数。很明显,在第一部分的话所有的叶子必须都是红色,也就是说,0-n个叶子上需要替换的就是黄色的叶子个数。那么我们可以直接通过f(n-1,0)这个函数得到的结果+当前位置的颜色。

f(n,1)表示从第0个到第n个叶子,同时第n个叶子处于第2部分的情况下,需要替换的最少的叶子个数。这个状态有点特殊,因为他可以从第f(n-1,0)状态转移得到也可以从函数f(n-1,1)状态转移得到。这么解释,首先,当前位置已经是第二状态了,那么是否需要调整只和当前节点是否是黄色有关。

f(n,2)表示从第0个到第n个叶子,同时第n个叶子处于第3部分的情况下,需要替换的最少叶子个数。这个状态也是可以由两种其他状态转移得到的。

2,如何获得结果

在设计好函数之后,我们第二步就是想如何得到我们的结果,也就是整个字符串的结果,就是f(i-1,3),i就是字符串的长度。

3,实现函数。

这一部分就是如何实现这个函数的效果,一般在设计的同时就会想到如何去实现这个函数的。

函数实现中需要注意的点。

上一步得到的结果我们是存在一个数组中的,因为先得到之前的结果,在进行下一步的计算,所以在计算的时候肯定已经得到了上一步的结果,所以可以直接使用。而所谓的f(n,a)这个函数的结果就是数组f[n][a]这个位置的值。

做的时候还出现了一个问题,就是三目运算符的优先级低于基础的加减运算法,这一点需要注意
在这里插入图片描述

public int minimumOperations(String leaves) {
   

    char[] chars = leaves.toCharArray();

    int len=leaves.length();

    //分别存三种状态时,某位置的最小调整次数
    int[][] vals=new int[len][3];

    //第一个位置需要自己手动填入,如果是红则不用替换
    vals[0][0] = chars[0] == 'r'? 0 : 1;
    //第一个不可以是第二部分,或者是第三部分,所以设置为调整次数最大值
    vals[0][1] = Integer.MAX_VALUE;

    for (int i=1;i<len;i++){
   

        //如果在第一部分,就看是否是红色,不是红色就替换
        vals[i][0] = vals[i-1][0] + (chars[i] == 'r'? 0 : 1);

        //如果在第二部分,找出前两种状态替换次数少的那个,加上当前是否是红色,是红色加就替换一次
        vals[i][1] = Math.min(vals[i-1][0],vals[i-1][1]) + (chars[i] == 'r'? 1 : 0);

        //第三部分的前提条件,必须前面已经有两个位置出现过,所以添加前置判断
        if (i >= 2){
   
            vals[i][2] = Math.min(vals[i-1][1],vals[i-1][2]) + (chars[i] == 'r'? 0 : 1);
        }else if (i == 1){
   
            vals[i][2] = Integer.MAX_VALUE;
        }

    }


    //动态规划完成,输出vals[len][2]的值
    return vals[len-1][2];
}

rrryrrrrrrrrrrrrrrrrrrrrrrry…

如果

10月2日 宝石与石头

在这里插入图片描述

class Solution {
   
    public int numJewelsInStones(String J, String S) {
   

    }
}

解题思路:

这道题简单,用哈希表存J中的每一个字符,或者使用数组代替哈希表也可以,然后遍历字符串S,去数组或者哈希表中寻找是否存在,如果存在res+1。

代码实现:
在这里插入图片描述

public int numJewelsInStones(String J, String S) {
   
    //128能表示所有字符
    boolean[] map=new boolean[128];

    int len1=J.length();
    int len2=S.length();
    int res=0;

    for (int i=0;i<len1;i++){
   
        map[J.charAt(i)]=true;
    }
    for (int j=0;j<len2;j++){
   
        if (map[S.charAt(j)])
            res++;
    }
    return res;
}

10月3日 两数之和

在这里插入图片描述

class Solution {
   
    public int[] twoSum(int[] nums, int target) {
   

    }
}

解题思路:

用一个哈希表来存他们的值和下标,以值作为键,下标作为值,遍历数组Nums然后存入哈希表。

然后再次遍历数组,求出和target的差值,并且判读这个差值是否存在于哈希表中,如果存在,则找到。

在这里插入图片描述

    public int[] twoSum(int[] nums, int target) {
   

        HashMap<Integer,Integer> numsMap=new HashMap<>();
        
        for (int i=0;i<nums.length;i++){
    
        
            if (numsMap.get(target-nums[i]) != null
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值