leetcode《图解数据结构》刷题日志【第四周】(2022/11/07-2022/11/20)
admin
2024-02-04 05:35:49
0

leetcode《图解数据结构》刷题日志【第四周】(2022/11/07-2022/11/20)

  • 1. 剑指 Offer 19. 正则表达式匹配
    • 1.1 题目
    • 1.2 解题思路
    • 1.3 数据类型功能函数总结
    • 1.4 java代码
    • 1.5 踩坑小记
  • 2. 剑指 Offer 42. 连续子数组的最大和
    • 2.1 题目
    • 2.2 解题思路
    • 2.3 数据类型功能函数总结
    • 2.4 java代码
  • 3. 剑指 Offer 46. 把数字翻译成字符串
    • 3.1 题目
    • 3.2 解题思路
    • 3.3 数据类型功能函数总结
    • 3.4 java代码
  • 4. 剑指 Offer 47. 礼物的最大价值
    • 4.1 题目
    • 4.2 解题思路
    • 4.3 数据类型功能函数总结
    • 4.4 java代码
    • 4.5 踩坑小记
  • 5. 剑指 Offer 48. 最长不含重复字符的子字符串
    • 5.1 题目
    • 5.2 解题思路
    • 5.3 数据类型功能函数总结
    • 5.4 java代码
    • 5.5 踩坑小记
  • 6. 剑指 Offer 49. 丑数
    • 6.1 题目
    • 6.2 解题思路
    • 6.3 数据类型功能函数总结
    • 6.4 java代码

1. 剑指 Offer 19. 正则表达式匹配

1.1 题目

请实现一个函数用来匹配包含'. '和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但与"aa.a"和"ab*a"均不匹配。示例 1:
输入:s = "aa"p = "a"
输出:false
解释: "a" 无法匹配 "aa" 整个字符串。示例 2:
输入:s = "aa"p = "a*"
输出:true
解释:?因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。示例?3:
输入:s = "ab"p = ".*"
输出:true
解释:?".*" 表示可匹配零个或多个('*')任意字符('.')。
示例 4:
输入:s = "aab"p = "c*a*b"
输出: true
解释:因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。示例 5:
输入:s = "mississippi"p = "mis*is*p*."
输出:false注意:s?可能为空,且只包含从?a-z?的小写字母。p?可能为空,且只包含从?a-z?的小写字母以及字符?.?和?*,无连续的 '*'。

1.2 解题思路

自己的思路是遍历模式字符串p,在遍历到某种符号的时候,顺便遍历待判断的字符串s,直到该模式失效。

  1. p[i]=='*',查看s当前字符和前一个字符是不是相等,用循环遍历所有相同字符。
  2. p[i]=='.',s跳过当前字符。
  3. p[i]==其他字符,和s中当前字符进行比较。
    然鹅,这样无法得到组合模式,比如.*之类的,虽然可以在原有的结构上修修补补,但是字符串的变种过多,个人根据这种简单的思路写出来的代码最多能够满足366/448种情况。
    而在官方题解中,考虑动态规划的最优子结构,从子问题的结果结合新添加的字符分情况讨论。对应状态d[i][j],可添加字符s[i-1]或者p[j-1]
    添加p[j-1]=='*',则考虑p[j-2]出现0次、1次的情况,以及p[j-2]=='.'的情况,出现任何一种情况都算正确。
    p[j-2]出现0次:则比较s[:i-1]以及p[:j-3]的匹配关系,对应的状态是d[i][j-2]
xxxxx a
xxxxx a b *
//比较的部分是"xxxxxa"和"xxxxxa",添加p[i-1]

p[j-2]出现1次:则需要考虑s[:i-2]以及p[:j+1]的匹配关系(s中不包含s[i-1],而p中包含p[j-2],也就是添加字符s[i-1]的情况,而p[j-2]出现0次是添加p[j-1]的情况),对应的状态是d[i-1][j],比较s[i-1]p[j-2](*前一个字符)的关系是不是相等或者p[j-2]是不是等于'.'

xxxxx a a
xxxxx a *
//比较的部分是"xxxxxa"和"xxxxxa*",添加s[i-1]

添加p[j-1]!='*',考虑s[:i-2]以及p[:j-2]的匹配关系,也就是s[i-1]p[j-1]之前的字符串比较,且待添加的两个字符需要相等或者p[j-1]=='.'

1.3 数据类型功能函数总结

//字符串相关操作
string.length();//获得字符串长度
string.charAt(index);//获得对应下标的字符

1.4 java代码

//官方解法
class Solution {public boolean isMatch(String s, String p) {int m = s.length() + 1, n = p.length() + 1;boolean[][] dp = new boolean[m][n];dp[0][0] = true;for(int j = 2; j < n; j += 2)dp[0][j] = dp[0][j - 2] && p.charAt(j - 1) == '*';for(int i = 1; i < m; i++) {for(int j = 1; j < n; j++) {dp[i][j] = p.charAt(j - 1) == '*' ?dp[i][j - 2] || dp[i - 1][j] && (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.') :dp[i - 1][j - 1] && (p.charAt(j - 1) == '.' || s.charAt(i - 1) == p.charAt(j - 1));}}return dp[m - 1][n - 1];}
}

1.5 踩坑小记

  1. java.lang.StringIndexOutOfBoundsException: String index out of range: 2
    数组访问越界,原因在于:
//错误代码
while(s.charAt(si-1)==s.charAt(si) && sisi++;
}
//错误点在于先进行数组访问再进行下标越界的判断,会导致数组访问出现越界。
//正确代码
while(sisi++;
}

2. 剑指 Offer 42. 连续子数组的最大和

2.1 题目

2.2 解题思路

个人思路:
动态规划,分解成子问题,a[:n]的子数组和最大值和a[:n-1]有关。

关系分析:a[:n-1]的子数组最大值为x,下标起始为index,添加a[n]之后,如果a[index:n]的和大于x,a[n]的子数组最大值为a[index:n],否则子数组最大值为x。

由于存在负数,如果子数组最大值为负数,则下次直接选择a[n]作为子数组最大值。

但是这种想法还是存在漏洞,通过样例171/202。

官方题解里面将状态设置为dp[i]为以nums[i]为结尾的前数组段的子数组最大值。
和我的想法不同的是,这个dp[i]必须包含nums[i],从而保证连续性。因此需要一个dp[0:n-1]的数组存储每一小段的结果,从而求出最大值。dp[i]状态转移根据dp[i-1]和新加入的nums[i]来确认,如果dp[i-1]为负数,说明dp[i]中如果加入dp[i-1]只会帮倒忙,因此dp[i]=nums[i];如果dp[i-1]为正数,则可以加到结果中,即dp[i]=dp[i-1]+nums[i];

降低空间复杂度之后,改写nums[]数组的每一项来表示dp[i]的结果。这样不用开辟dp[n]的数组空间,提高效率。

2.3 数据类型功能函数总结

//数组
int[] array_Name=new int[size];//构建空数组
int n=array.length;//获得数组长度

2.4 java代码

class Solution {public int maxSubArray(int[] nums) {int n=nums.length;//构建dp[i]最优子结构,可以直接修改nums[]数组for(int i=1;iif(nums[i-1]<=0){nums[i]=nums[i];}else{nums[i]+=nums[i-1];}}//查找所有结果中的最大值。int max=nums[0];for(int i=1;iif(maxmax=nums[i];}}return max;}
}

更加精简的代码:

class Solution {public int maxSubArray(int[] nums) {int n=nums.length;int max=nums[0];//一次遍历,一边修改nums[]数组保存结果,一边记录目前的最大值for(int i=1;iif(nums[i-1]>0){nums[i]+=nums[i-1];}if(nums[i]>max){max=nums[i];}}return max;}
}

第二种写法内存消耗小了0.2MB,看似不起眼,但是把超越人数从19%拉到了47%。

3. 剑指 Offer 46. 把数字翻译成字符串

3.1 题目

给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。示例 1:输入: 12258输出: 5解释: 12258有5种不同的翻译,分别是"bccfi", "bwfi", "bczi", "mcfi"和"mzi"提示:0 <= num < 231

3.2 解题思路

这题的分解情况和青蛙跳台阶的问题比较类似,青蛙跳台阶需要跳1层或者2层。

这里对于规模为n的数字,可以分解为规模为n-1的子问题和规模为n-2的子问题的和。初步的状态设计如下如下:

dp[i] 长度为i的数字的翻译方法
dp[0]=1,dp[1]=1,dp[2]=2,……
dp[i]=dp[i-1]+dp[i-2]

不过,结合提议,对于123149这两个数字,分解翻译的结果就不尽相同,还需要考虑规模为n的数字能不能拆分为规模为n-2的子问题。
先来考虑状态设计:

dp[i] 长度为i的数字的翻译方法
dp[0]=1,dp[1]=1,dp[2]=2,……
//状态转移公式
dp[i]=dp[i-1]+dp[i-2];————如果能拆分,则跟上述分析类似
dp[i]=dp[i-1];————如果不能拆,则dp[i]的结果完全都dp[i-1]决定。仅有一种翻译方法。

下面考虑如何确定能不能拆分为n-2的子问题:

需要考虑n-2和n之间的数字和‘25’的大小关系。每次需要根据当前位数下标n,判断n-1和n组成的数字是不是在0~25的范围内。
因此,考虑将int型数组转为字符串s,并对两个字符串可能出现的情况进行讨论:
如果s[n-1]为1,或者s[n-1]为2但是s[n]为0~5,则表示n可以分解为n-2子问题;
否则,表示n不能被分解为n-2子问题。
特别说明:对于s[n-1]为‘0’的情况,0x虽然是一个小于25的数,但是不能按照0x表示的数字来翻译,不然则会丢掉‘0’的翻译结果,因此这一种情况需要0和x分别翻译,算在不能分解为子问题的情况里

3.3 数据类型功能函数总结

//字符串类型
String s=String.valueOf(int);//int型转字符串s
String.length();//获取字符串长度
string.charAt(index);//获取对应下标的字符

3.4 java代码

class Solution {public int translateNum(int num) {int dp_2=1,dp_1=1;int dp=0;if(num==0){//0dp=1;}else if(num/10==0){//个位dp=1;}else if(num/100==0){//两位数dp=2;}String num_s=String.valueOf(num);//将数字转为字符串。for(int i=1;i//122 12 1char a1=num_s.charAt(i-1);char a2=num_s.charAt(i);//进行状态转移:if(a1=='1'){dp=dp_2+dp_1;dp_2=dp_1;dp_1=dp;}else if(a1=='2'&& a2>='0' && a2<='5'){dp=dp_2+dp_1;dp_2=dp_1;dp_1=dp;}else{//不能分解为n-2dp=dp_1;//dp=1+1=2 3 5dp_2=dp_1;//dp2=1 2dp_1=dp;//dp1=2 3}}return dp;}
}

4. 剑指 Offer 47. 礼物的最大价值

4.1 题目

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?示例 1:输入: [
?   [1,3,1],
?   [1,5,1],
?   [4,2,1]]输出: 12解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物
?
提示:0 < grid.length <= 2000 < grid[0].length <= 200

4.2 解题思路

动态规划问题,设dp[i][j]表示移动到grid[i][j]时得到的最大价值,最终取dp[m-1][n-1]的值能够得到最大值。

  • 最优子结构导出的状态转移式为:dp[i][j]=max{dp[i-1][j]+grid[i][j],dp[i][j-1]+grid[i][j]}
  • 初始状态dp[0][0]=grid[0][0]

连续子数组的最大值那题类似,这题的状态变化只跟下一次要移动的格子里面的礼物价值相关,因此可以选择覆盖原有棋盘从而降低空间复杂度。

对于首行或者首列,从0 0位置移动仅有一种方式,因此首行和首列需要和上述的状态转移值分开考虑。在遍历过程中判断i-1,j-1有没有越界就可以实现

4.3 数据类型功能函数总结

//二维数组相关操作
int line=array2.length;//获得二维数组行数
int row=array2[0].length;//获得二维数组列数

4.4 java代码

class Solution {public int maxValue(int[][] grid) {int m=grid.length;//行数int n=grid[0].length;//列数//计算dpfor(int i=0;ifor(int j=0;j//遍历第i行if(i-1>=0){//非首行if(j-1>=0){//非首列元素int value=grid[i][j];grid[i][j]=Math.max(grid[i-1][j]+value,grid[i][j-1]+value);}else{//首列元素grid[i][j]+=grid[i-1][j];}}else{//首行if(j-1>=0){//不是第一个元素grid[i][j]+=grid[i][j-1];}//首行第一个元素不需考虑,是grid[0][0]}}}return grid[m-1][n-1];}
}

4.5 踩坑小记

1.java. lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
数组越界,仔细检查可知:自己的return是返回grid[m][n];其中m、n分别为行数和列数,实际上应该写出grid[m-1][n-1];

5. 剑指 Offer 48. 最长不含重复字符的子字符串

5.1 题目

5.2 解题思路

按照动态规划的思路,需要将问题分解为子问题。

最开始的思路:假设dp[i]指的是长度为i的字符串的无重复字符串长度,则dp[i+1]是从dp[i]增加一个s[i+1].
如果s[i+1]在字串里面有重复字符串,dp[i+1]=dp[i],,如果s[i+1]不重复,dp[i+1]=dp[i]+1

但是,这个最长连续子数组的最大和的题目类似,由于需要考虑连续字串,所以这里可以考虑修改dp[i]的定义是包含s[i]的前段字符的最长字串长度。
这样如果添加s[i+1],如果s[i+1]重复,dp[i+1]=1;如果不重复,dp[i+1]=dp[i]+1;

之后考虑重复字符串如何查询,最简单的思路是使用遍历,或者使用特殊的数据结构,比如集合set

再经过考虑,重复的时候dp[i+1]应该不是直接等于1,而是根据s[i+1]和之前的字符串中重复的位置来确定。所以用字符串遍历比用set更好,或者用list列表

5.3 数据类型功能函数总结

//字符串相关操作
string.length();//获取字符串长度
string.charAt(index);//获取对应下标的字符
//arraylist相关操作
ArrayList<> al_name=new ArrayList<>();//定义一个列表
ArrayList.add(elem);//在当前list末尾添加元素
ArrayList.remove(index);//删除下标为index的元素,后面的元素左移
ArrayList.indexOf(elem);//查找元素下标,没有的元素则返回-1

5.4 java代码

class Solution {public int lengthOfLongestSubstring(String s) {if(s.length()==0){//特殊情况处理return 0;}int dp=1;//初始值int max=dp;//最大长度ArrayList list= new ArrayList();list.add(s.charAt(0));for(int i=1;iint index=list.indexOf(s.charAt(i));if(index!=-1){//找到该元素,说明重复for(int j=0;j<=index;j++){list.remove(0);}list.add(s.charAt(i));dp=dp-index;}else{//不重复dp+=1;list.add(s.charAt(i));}if(dp>max){max=dp;}}return max;}
}

5.5 踩坑小记

1、java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 1
错误是下标访问越界的错误,考虑代码,我错误的地方在于删除list中数据的时候,以为remove()之后后面的元素不会自动左移,所以按照一般静态数组的方式来删除元素。
//错误代码如下:
for(int j=0;j<=index;j++){list.remove(j);
}
//随着数据左移而j不断增加,最后会出现数组越界的情况
//正确代码如下:
for(int j=0;j<=index;j++){list.remove(0);
}

6. 剑指 Offer 49. 丑数

6.1 题目

我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。示例:输入: n = 10输出: 12解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。说明:  1是丑数。n不超过1690。

6.2 解题思路

最简单的思路:从1遍历,直到找到第n个丑数,结束遍历。

如果要用动态规划来求解的话,需要找到第n-1个丑数和第n个丑数之间的关系
假设dp[n]表示第n个丑数,则dp[n-1]和dp[n]的关系为:

  • 如果dp[n-1]+1不满足丑数定义,一直加1直到满足为止。dp[n-1]+x=dp[n];
  • 如果dp[n-1]+1满足丑数定义,dp[n-1]+1=dp[n]
    再考虑初始情况,第一个丑数dp[1]=1;

丑数的判定则需要考虑是不是只含有2、3、5作为因子。如果num/2或3或5的结果也是丑数,则num是丑数。

但是后面用这种方法做的时候比较困难。最后还是根据官方题解的思路做的。

官方题解里面也是有一个思想:如果num/2或3或5的结果也是丑数,则num是丑数。所有dp[a]、dp[b]、dp[c]里面一定有一个是dp[i]%2或3或5得到的。这三个子问题求解得到dp[i];
且随着i的增加,子问题也需要不断前进更新,如果dp[x]*y==dp[i],则x这个下标需要往前进一个,这样才能保证没有重复值。

6.3 数据类型功能函数总结

//Math
Math.min(a,b);//比较两个数的最小值
//数组相关
int dp[] = new int[Size];//定义一维数组

6.4 java代码

class Solution {public int nthUglyNumber(int n) {int dp[]=new int[1690];int a=0,b=0,c=0;dp[0]=1;for(int i=1;idp[i]=Math.min(dp[a]*2,dp[b]*3);dp[i]=Math.min(dp[i],dp[c]*5);if(dp[a]*2==dp[i]){a++;}if(dp[b]*3==dp[i]){b++;}if(dp[c]*5==dp[i]){c++;}}return dp[n-1];}
}

相关内容

热门资讯

黄山5天4晚行程推荐,黄山旅游... 黄山,这座屹立于皖南大地的奇峰峻岭,宛如一幅流动的山水画卷,自古以来便吸引着无数文人墨客为之倾倒。它...
金毛轮胎上越战越勇节目是哪一期... 金毛轮胎上越战越勇节目是哪一期?还没有播出吧下一期2017****期
程旅国盛(北京)文化发展有限公... 程旅国盛(北京)文化发展有限公司:三亚,碧海蓝天间的热带风情 三亚,这座镶嵌在中国南海之滨的璀璨明珠...
宜宾这些镇火了!有你家乡吗? 宜宾市乡村旅游点综合人气指数由宜宾市乡村旅游点六月内旅游人次、宣传报道热度、活动开展情况三个指标共同...
逻辑和常识有什么区别 逻辑和常识有什么区别逻辑和常识有什么区别逻辑: 1规律,事物的完成的序列。 2:事物流动的顺序规则 ...
原创 出... 夏天,最让人心动的事情之一,绝对是驾着游艇出海,享受阳光、海风,还有那片蔚蓝的大海。想象一下,穿上清...
原创 林... 今天,女神林志玲被偶遇带着孩子和老公黑泽良平,一家三口在环球影城游玩。 视频中的林志玲,扎着大大的麻...
原创 陈... 昨日,陈熙蕊在社交媒体上热情分享了一组崭新的旅行写真,照片中她和妈妈一起畅游多个国外知名网红打卡景点...
八尾猫的家乡之旅作文 八尾猫的家乡之旅作文之夜满足一个善良人的愿望,之后便长出一尾,九尾既成仙。雪球知道这个方法后,决定离...
魅力南疆12天11晚小团游:邂... 魅力南疆12天11晚小团游:邂逅巴楚红海景区的独特风光 新疆旅游咨询: 小敏18509915393 ...
AI数字人、VR大空间、MR任... 在山西博物院,虚拟星推官“青鸟”身着晋绣华服,向游客讲述三晋文明;在伊犁将军府,数字人“将军”耐心解...
文水:景中有“警”讲安全 筑牢... 随着学生暑假来临,景区旅游高峰期也来临,为持续做好旅游季道路交通安全管理工作,确保辖区景区交通安全持...
希腊网红游名城:最喜欢成都,天... “这边的环境太好了!”“这里是野餐聚会的绝佳场所,非常浪漫。”7月5日,玛丽安(Mariana)和索...
漫长的季节想表达什么 漫长的季节想表达什么漫长的季节想表达什么:漫长的季节讲的是关于时间的流逝和内心的变化。它催促我们从未...
亲子游带动暑期市场结构性升级 亲子游驱动暑期旅游市场结构性升级:IP赋能、服务创新与消费分层成新趋势 2025年暑期旅游市场迎来...
龙飞凤舞的意思是什么? 龙飞凤舞的意思是什么? 龙飞凤舞原形容山势蜿蜒起伏,气势磅礴。现多指书法笔势有力,灵活奔放。出处:宋...
真正经历过的饥荒有多可怕 真正经历过的饥荒有多可怕如果从历史来看,世界上不管什么制度的国家,没经历过饥荒的国家估计少之又少