Algorithms&Data Structures保研

LeetCode机试练习

和百练机试格式一样,百练上的中文题已经刷得差不多了,剩下的英文题不好定义难度,过于简单和过于难的机试都不会考,反而LeetCode标了难度为medium的都在机试范围内,虽然机试不建议刷LeetCode,因为有很多机试题重点在于数据处理,而LeetCode正好帮助新手避免了这个问题,但是因为我是老司机了(bushi,所以还是刷LeetCode了,以medium为主

目录:日期+题号+题目大意+题解+总结

2021.3.31

3:无重复字符的最长子串

题目大意:如题

一开始以为只有小写字母,直接二分答案+O(n)判断,结果还有数字字符乱七八糟的,我直接开256布尔数组,老暴力了,时间复杂度O(256nlogn).

总结:①其实没啥好总结的,但是前段时间在忙论文得有一个月没做算法题,然后晚上脑子直接瓦特了,反正调了好久

5:最长回文子串

题目大意:如题

md一开始以为又是一个二分,刷刷一通写,后来发现并不满足二分的性质,盲猜dp一看题解果然是,直接dp判断i~j的子串是不是回文串就好了,转移方程还是很好想的,但是我一开始想错方向了,高级做法是manacher,有时间再更新学习笔记吧

2021.4.1

6:Z字形变换

题目大意:给出一个字符串,以倒立Z字形排列,然后按行按列从左到右输出排列后的字符串

直接模拟,根据一个Z字形判断需要多少个字符,然后根据行数进行赋值即可

总结:①忘了考虑n可能为0的情况,导致除0RE

1006:笨阶乘

题目大意:把原先阶乘计算的乘号全部换成乘除加减,然后计算改变后的阶乘

可以发现乘除挨在一块永远是一块计算的,然后算完的数剩下的只剩加减法了,无论是用栈还是队列都可以直接加减加减循环计算,我这里用的是队列

2021.4.2

8:字符串转换整数

题目大意:如题

实际上就是让你手动实现C++atoi,按照题目上的步骤一步步来就行,几个样例覆盖比较全面,边界用long long处理的,唯一sb的地方是计算字符串的时候不知道为什么nt地设置Pow数组来记录10^n,明明ans反复*10就能解决的问题,果然年纪大了QAQ

2021.4.3

1143:最长公共子序列

题目大意:子序列的定义改为不改变排列顺序删除任意个字符得到的字符串都为子序列,求最长公共子序列

果然dp一生之敌,一开始没往dp上面想,因为子序列的定义改了,感觉dp并不能表示所有状态(事实证明我错了),所以就把所有可能的序列都列举出来跑一个一维的最长上升子序列,结果被无数个a重复的数据卡TLE了,不过只要不是这种极限数据,一维最长上升子序列的复杂度应该能完爆普通数据,就是极限的情况就不行了,所以还是二维dp吧,状态转移方程就不说了,稍微想想就有了

总结:①尝试所有可能的解决方案,对dp不熟所以没有一下子想到dp,还需要多练

2021.4.4

781:森林中的兔子

题目大意:每个兔子有一个颜色,给出一个序列,序列中的每个值是与当前兔子颜色相同的兔子数,求最少的兔子数

其实是一个思维题,我一开始还以为套个并查集然后一查大小就完事了,后来发现这样做的问题是相同数的兔子可能并不是同一种颜色,比如说3个1肯定就至少是两个颜色,于是直接想到n个n-1肯定是同一种颜色,把所有颜色的数目统计一下直接累加就可以了

12:整数转罗马数字

题目大意:如题

把题目中列的罗马数字,加上特殊的4、9、40、90什么的全都放进map里转成罗马字符串,然后从大到小循环找比当前数小的最大数去减,减一个输出一个对应的罗马数字即可

2021.4.5

15:三数之和

题目大意:求序列中的三元组使得三元组之和为0,三元组不能重复

其实O(n^2)很好想,我想的是枚举两个数xy,然后判断-x-y是否也出现在序列中,然而这样做很大问题出现在判断重复上,T了无数次终于发现自己脑子瓦特了,一开始我的做法是疯狂上STL,用map映射、二分查找、set去重,然后push进vector的时候按顺序push,还写了个三个数从小到大排序,然而对于大量的0这个数据T了,然后我又离散化手动map,这样查找时间复杂度O(1)了,但是还是T,可能3k的数据量对于常数太大的程序在lc上容易T,后来想明白了,既然记录了每个数出现的次数又何必再遍历原数组呢,直接遍历去重后的数组不就可以了么,然后就A了

总结:①STL常数太大,能尽量不用就不用②吐槽lc数据范围标的10^5,结果加上10^5还有负数

2021.4.6

16:最接近的三数之和

题目大意:找出三个数使得三数之和接近target,求这三个数的和

二分答案,二分与target的差x,然后查找target-x和target+x之间是否有数存在,这个可以用前缀和实现,时间复杂度O(n^2logn).

80:删除有序数组中的重复项 II

题目大意:在O(1)空间复杂度下原地删除出现超过2次的数

超过2次的数标记为inf,然后用vector.erase删除这个数,删除的过程中vector.size()会有变化,所以用while循环来进行删除

2021.4.7

81:搜索旋转排序数组 II

题目大意:将一个数组按某个位置旋转求值为target的数是否在数组中

应该是让练习二分查找的,但是我一看复杂度O(n)能过我就直接遍历了。。

17:电话号码的字母组合

题目大意:如题

字符串最大长度为4,也就是最多四重循环,直接上循环

2021.4.8

153:寻找旋转排序数组中的最小值

题目大意:如题

又是一道二分查找的题,我又直接遍历了。。

19:删除链表的倒数第N个结点

题目大意:如题

其实进阶做法就是快慢指针,但是由于链表的题实在不知道该怎么写,只会纸上谈兵,不得已参考了一下std,发现标算都是先设一个头指针指向链表首结点,然后ans赋值为头指针,然后delete头指针返回ans

2021.4.10

102:二叉树的层序遍历

题目大意:如题

实际上层序遍历就是每一层从左到右遍历,本质上还是bfs,所以用队列记录一下每个结点所在的深度,然后相同深度的push_back即可

103:二叉树的锯齿形层序遍历

题目大意:如题

实际上就是102奇数层正着扫偶数层反着扫,我们已知每个层的大小之后直接在索引把结点值赋上即可

2021.4.11

78:子集

题目大意:给出一个集合求所有子集

dfs,用flag记录当前元素选了或没选,当前元素个数>n时输出方案

90:子集 II

题目大意:与上题不同的是集合中可能有重复元素,输出的子集要去重

在上题的基础上对集合sort一下然后放入set去重

2021.4.12

179:最大数

题目大意:给出一些整数,求这些整数连接在一起产生的最大值

其实挺简单的一道题,但是中间有个细节想错了,比如3和34连接,应该是343而不是334,其实比较的就是两个字符串连接之后哪个比较大,排序即可,而不是讨论34和3的最后一个字符的大小,直接根据字符串连接来排序即可,排的序是带有传递性的

264:丑数 II

题目大意:只能被2、3、5整除的数为丑数,求第n个丑数是哪个数

调一下测试用例就能知道第1690个数大约接近int的最大值,所以直接O(n)打表即可(雾

其实是我并不会正解,看了一下正解用的最小堆nlogn和dp O(n)的,没太看懂,但是打表是真的快,30s就打出来了

打表程序

O(1)这难道不是正解?

2021.4.13

113:路径总和 II

题目大意:找出从树根到叶子结点的路径之和等于target的所有路径

dfs,找到叶子结点时判断路径之和是否等于target,不行就回溯找下一个

131:分割回文串

题目大意:在字符串中切割任意刀,使得每个子串都是回文串,输出所有方案

dfs枚举出所有刀切的位置,就能得到所有切割方案,然后根据回文串的dp预处理可以O(1)判断是否是回文串

2021.4.14

200:岛屿数量

题目大意:给出01矩阵,求相连的1的块数

直接bfs然后设标记数组即可

207:课程表

题目大意:修某一门课需要把所有先修课程修完才能修,给出n个课程和若干个依赖关系,问能否全部修完

拓扑排序,之前一直没弄懂用邻接表怎么写,看到blog里说每次把入度为0的点放到队列里,每次dfs减去相邻点入度-1,用标记数组判断是否搜索过,如果搜索过表示有环存在返回false,否则为true

2021.4.15

213:打家劫舍 II

题目大意:一个环形序列,每个序列上有权值,只能取不相邻的权值求能取到的最大值

环形dp,可以把序列拆成n个链状序列来跑dp,dp[i][0/1]表示当前第i个0表示不取,1表示取的最大值,转移方程dp[i][0]=max(dp[i-1][1],dp[i-1][0]),dp[i][1]=dp[i-1][0]+a[i].

总结:①i+nums.size()-2可能小于0,这时数组越界应该加条件判断

210:课程表 II

题目大意:课程表那个题原题,现在需要输出修课的序列

课程表那个题拓扑排序稍微改改

2021.4.16

63:不同路径 II

题目大意:n*m矩阵中间有一些障碍物,求(1,1)走到(n,m)的方案数

直接dp

64:最小路径和

题目大意:(1,1)走到(n,m)的最小路径和

直接dp

2021.4.17

220:存在重复元素 III

题目大意:给出k和t,求是否存在两个不同下标i和j,使得abs(num[i]-num[j])<=t并且abs(i-j)<=k

先给序列排序,带id的cmp排序,然后对于每个a[i]就可以找到差的绝对值<=t的区间,然后在这个区间内找是否存在id差值在k以内的,直接用双指针指就可以了,一开始还以为是线段树,来求最大值最小值的思路是错误的

2021.4.19

300:最长递增子序列

题目大意:如题

直接dp

2021.4.20

279:完全平方数

题目大意:给出一个数,求能用完全平方数相加的最小数量

n最大是1w,也就是说完全平方数最大为100,先用二分找到比n小的完全平方数是第几个,然后dp[i]表示用完全平方数表示i所用的最小数量,实际是一个完全背包问题,dp[j]=min(dp[j],dp[j-a[i]]+1),时间复杂度O(nm),m表示比n小的完全平方数是第m个

983:最低票价

题目大意:给出旅行行程,表示第几天去旅行,可以买1天、7天和30天的旅行通行证,每种通行证都有花费,求完成全部旅程的最小花费

比较显然的一维dp,dp[i]表示完成前i个行程的最小花费,则转移方程为dp[i]=min(dp[p1]+costs[0],dp[p2]+costs[1],dp[p3]+costs[2]),其中p1,p2,p3分别表示第几天买的1天、7天、30天通行证,因为用了二分查找,时间复杂度O(nlogn).

2021.4.21

91:解码方法

题目大意:大写字母A到Z可以映射数字1到26,给出一个数字串,求有多少种由大写字母映射的数字串

可以先dfs暴搜出所有分割字符串的方法数,然后判断是否在1~26之间,保留出所有合法的方法就是答案,但是暴搜会超时,可以把暴搜改成记忆化搜索,用dp[x]记录当前在x位置分割的方法数,如果搜索过直接将答案返回即可

97:交错字符串

题目大意:给出三个字符串,求前两个字符串是否能完美交错匹配第三个字符串

自从dfs暴搜运用熟练之后,现在越来越喜欢记忆化搜索了,把暴搜打出来然后记忆化就A了,暴搜需要三个状态,当前匹配的第三个字符串位置i,匹配了第一个字符串j个字符,匹配了第二个字符串k个字符,然后边界条件是i,j,k分别等于三个字符串长度则返回true,否则返回false

总结:①标记是否搜索过的flag数组要在dfs之后设为true②边界条件要各种情况都考虑到,不能只考虑为true的情况

2021.4.22

139:单词拆分

题目大意:求是否能将一个字符串分成若干单词使得所有单词都在词表中

又是dfs+记忆化,但是这里写的时候出现了一些问题,按照之前写的分割字符串,然后最后对所有单词进行判断然后返回结果,用dp数组记录的时候没有在分割时判断是否在词表中,这个思路是错误的,原因是一开始分割的字符串极有可能是不满足条件的,然后判断为false之后赋给了dp数组,等到下次dfs到这个位置的时候就直接返回false了,并没有继续下一个尝试

221:最大正方形

题目大意:给出n*m的01矩阵,求全为1的最大正方形的面积

最大正方形满足一个性质,最大正方形也包含次大正方形,所以在找最大正方形面积时可以一步步递增去判断是否满足条件的,进而满足二分答案的条件,所以二分正方形边长,预处理前缀和,然后判断枚举正方形的任意一个端点即可,时间复杂度O(nmlog(min(n,m))).

2021.4.24

377:组合总和 IV

题目大意:给出互不相同的整数序列,求总和为target的组合个数

dfs暴搜还是非常好打的,然后就是dfs改记忆化,这部分也是昨天做题一直在困扰我的地方就是改成记忆化不好改,其实注意到一点是在每个dfs的过程中sum是一直在记录信息的,应该也作为状态之一,但是dp数组里没有记录sum,所以就不能完美表示所有状态

877:石子游戏

题目大意:偶数堆石子,两个人每人每次取一堆,总数为奇数,问是否有必胜策略

博弈论也是之前一直薄弱的地方,今天还专门找了篇博客学了学,大体理解了博弈论的中心思想,改天写篇博客总结一下,这个题最终结果是先手必胜的,上午的时候没有想明白原因,晚上的时候突然顿悟了,因为石子总数是奇数,而堆数是偶数,那么可以按奇数堆和偶数堆划分为两组,因为总数是奇数所以肯定有一组石子更多,无论后手怎么选,先手只要一直选那组石子更多的就可以必胜

2021.4.25

40:组合总和 II

题目大意:求总和为target的所有组合,每个数只能选一次

求方案的只能dfs回溯,记录sum大于target剪枝即可,有更简便的方法可以dfs,但是目的是练回溯就不细究了

319:灯泡开关

题目大意:初始n个灯泡关闭,第i轮每i个灯泡切换状态,即第一轮每个灯泡切换状态,第二轮每两个灯泡切换状态,求n轮之后灯泡为开启的个数

一看到n的范围是10^9基本就知道是有简便方法的了,但是我一开始没看出来,非常显然的一个性质是第i个灯泡n轮之后的状态与i的因子个数有关,如果是奇数就亮偶数就暗,所以通过判断1~n的因子个数奇偶性即可,打了个暴力,发现答案正好等于\sqrt n,然后直接输出\sqrt n就A了,但是不知道证明,看了一下题解之后发现,只有完全平方数的因子个数是奇数,非完全平方数的因子个数都是偶数,由一个数的因子个数的一半分布在1\sim\sqrt n之间,所以非完全平方数的因子个数就算1\sim\sqrt n之间的因子个数*2,肯定是偶数,完全平方数的因子个数就是奇数

2021.4.26

372:超级次方

题目大意:求a^b\; mod\; 1337的值,a的范围是int,b的范围是2000位整数

一看就是快速幂,然后准备用大整数取模,在网上搜了一下板子,基本上都是用秦九韶算法来取模的,但是这里应该取模的是\varphi(1337),这里应该再复习一下欧拉定理,好像上次得到的那个结论有点问题,所以这里\varphi(1337)=1140,所以对1140取模即可,然后直接快速幂就出来了

1011:在D天内送达包裹的能力

题目大意:求在D天内按照顺序装载货物的最小装载重量

直接二分答案然后O(n)判断,时间复杂度O(nlogn).

2021.4.27

50:Pow(x, n)

题目大意:如题

这个是浮点数的快速幂,需要注意的是n的范围是int,很容易爆int,所以直接把int换成long long

397:整数替换

题目大意:奇数替换为n+1或n-1,偶数替换为n/2,求n=1时的最小替换次数

因为有n/2,所以复杂度肯定是O(logn)的,奇数替换n+1或者n-1需要dfs判断一下

总结:①范围在int的变量一定要换成long long,防止爆int

2021.4.28

462:最少移动次数使数组元素相等 II

题目大意:给一个序列,每次移动可以选一个元素+1或-1,求让序列所有数相等的最少移动次数

一开始写的二分,后来发现答案不是单调函数,就网上搜了一下三分的姿势然后写了三分,因为答案是单峰函数,然后根据两个mid的值大小关系判断即可

633:平方数之和

题目大意:求一个数能否表示为两个数的平方和的形式

直接O(\sqrt n)暴力判断即可

2021.5.2

310:最小高度树

题目大意:给出一棵树,求以哪个结点为根时树的深度最小

正解是树的直径,一开始我在想是树的重心,启发这个思路的原因是看到样例里面有一个或者两个结点,想到树的重心的性质是至多有两个重心,但是其实里面的一个样例已经能够hack掉这个想法了,深思了一下原因,树的重心是所有结点到该结点的距离之和最小,而不是深度最小,如果让深度最小应该是直径的中心才对,求树的直径只需要两遍dfs,具体证明网上有很多,这里就不详细证了,求完直径,在dfs的过程中记录一下每个结点的father,然后从直径的两个端点暴力往上找father即可

554:砖墙

题目大意:给出一系列砖的宽度,保证这些砖组成的每一行长度都相等,求在砖墙垂直划一道线所经过的最少砖数

很明显的一个性质是划线的时候只要是划线的这个位置前面是当前行砖的宽度和这道线就不会碰到当前行的砖,所以我们预处理出每行的砖的宽度前缀和,然后排序找一下出现次数最多的数有多少个,用行数减去这个数就是答案