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