LeetCode1-100个人总结
作者:互联网
1.两数之和各种不用实现的条件,很简单,粗心j的初始值写为了1,应为i+1。还有就是return直接{i,j},不必新建一个vector。
2.两数相加很有难度。主要是不了解struct链表的结构和操作,ListNode res = new ListNode(0);这个是创建新链表的标准语句,此外,本题需要一个int型sum来全程记录list的具体值,并进行判断进位等。
3.无重复字符的最长字串不容小觑。思维大于代码量。思路,先给每个位置初始值设为-1,目的是该值小于string下标的任何一个(0–s.length())。然后遍历string,如果哪个位置大于start(-1)则将start置换为该值的i。只有当出现连续两个相同字符时才会出现这种情况,如ww,因为如果连续几个都不相同的话是不可能出现dict大于start=-1的,这样也保障之前是从第一个位置开始记录的,直到出现相同的。然后就要找终止位,终止位一般就是当前的i,让i-start就是最大的,一直遍历string就行了,让它动态的变化。4.寻找两个有序数组的中位数此题不难,记住sort(res,begin(),res.end())对vector进行排序。还有就是一个重要细节,double除的时候,必须加小数点,比如5/2就算是用double接收,返回的还是2,必须用5/2.0才能返回2.5用set自做效率还高。5.最长回文子串很有难度也很经典,用到了DP。暴力法复杂度为O(n3).动态规划复杂度为0(n2),且DP一定要先在纸上写出转移方程。这个很经典。此题dp[n][n]zai VC编译器下是不行的,而且还是个二维的,不能用new,所以得用vector<vector> dp;来代替。关键是maxlen来记录偏移量,且有个判断if(dp[j][i]&&maxlen<i-j+1)就是保证肯定是最长的回文子串。 此外还有一种马拉车方法,复杂度为O(n)。6.Z字形变换没啥意思的题目,但是很考验逻辑思维。判断跳转那很重要,还有些奇技淫巧比如返回值string是通过rows[curRow] += c;来获得的。curRow += goingDown ? 1 : -1;这个是精髓。7.整数反转题目本身非常简单,也不用整什么把int转化为string再运算,直接int。但是有个问题就是int溢出,这里要判断它的个位 if(ret > INT_MAX/10 || (ret == INT_MAX/10 && x >7)) return 0;if(ret < INT_MIN/10 || (ret == INT_MIN/10 && x <-8)) return 0;牢记这个判断溢出的语句!8.字符串转换整数(atoi)此题也不难,关键是溢出那个。还是那个核心语句if (ret > INT_MAX / 10 || (ret == INT_MAX / 10 && str[i] - ‘0’ > 7)) return (flag == 0) ? INT_MAX : INT_MIN;此外,再gcc和vc都可运行但leetcode不行,所以不用管了,是正确的。9.回文数此题本不难,但万万不可用int进行解答,必须转化为char字符串,因为有些特殊的1000021不是回文数,但是按照int方式解答的过程中会忽略掉其间的0儿变为2,最终答案是回文数,这就错了,所以因为考虑到中间很多0的情况一定要转化为字符串来解答。通过%去除后半段组成新int,与前半段对比是否相等。10.正则表达式匹配不会做。这题是用dp,但怎么也想不出转移方程,主要之前那个题是只有一个字符串,这个题是两个,觉得不可能用dp,但还是可以。事实上,dp有两种解决方案,比较简单的一个字符串的就是转移方程,还有一个就是画图了,化成一个长方形,行代表一个,列代表一个然后里面的小格子代表各种情况。用dp[i][j]表示字符串s的前i个字符和字符串p的前j个字符是否匹配,那么状态转移情况如下:if (dp[i-1][j-1] && (s[i] == p[j] || ‘?’ == p[j])) dp[i][j] = true, 即如果前i-1和前j-1个字符匹配,当前字符也匹配,那么前i个和前j个也匹配。要考虑的情况很多:1.s无字符,p无字符2.s无字符,p有一个字符(且不能省略)3.s有字符,p无字符4.Ax只有A与空串匹配,且后两个字符是x的形式才匹配5.如果不是,只有p遍历到.或者p[j-1]和s[i-1]相等的时候匹配6x表示0个x的情况7.x表示1个x的情况8.x表示多个x的时候表示,此时必须s[0~i-2]与p[0~j-1]匹配且…… 11.盛最多水的容器一开始没做出来,本题采用双指针法,一左一右往中间凑。谁小谁动,总面积等于两指针距离乘以最低的高度,凑到最后取过程中最大的面积即可。12.整数转罗马数字又沦陷了,此题中等,因为情况太多,不可能有个简单的算法来推的,所以要先写一个数组存放所有可能的字符,然后再创建一个数组存放位数;或者直接写一个4,10的二维数组(所有情况就这么多),第一位存放位数,第二维存放字符,再写一个res = table[k++][num % 10] + res;来存放转换后的string即可。此外,有个bug,不能写为res+=table[k++][num%10]。字符串运算不同于整形13.罗马数字转整数反向思维,本来比上题还要简单,因为需要把那几个字母转换为int就行了。但是自己太笨,还是借助答案。主要是没想到IV这种情况咋们弄,他不是加,而是减。其实只要弄一个临时值存放,然后如果左边的值小于右边的就减,因为边界的(s.length()-1)没有右边的无法判断,所以也是直接加。记住了啊14.最长公共前缀振奋人心啊,终于自做出来了!就是用暴力的方法,写一个函数匹配每两个的最长公共前缀,然后在solution里循环两个两个比较,每一个的结果都与下一个比较,最后缩短为最长公共子串。值得注意的是,又忘了考虑为空和为1的情况。15.三数之和这题肯定不能用暴力法,时间复杂度n3太大了,耐心思考下发现一定要凑起来,最好能从两边凑,这样冲突少,但是有三个数,所以以一个数为基准,从这个数右边到终点一直动来凑。当然,那个基准数在每一轮后也要继续向后移动。动到啥时候停呢?当这个数的值大于0的时候,为啥呢?因为我们首先讲数组从小到大排序了,如果第一个都大于0了那后面的全都是大于0的,怎么也凑不到0了,具体的这个停止线根据不同的题来变化,这题是三数之和为0所以才定位0.然后判断重复的话就continue不管。总而言之算上各种判断的话非常复杂,所以在大体思路出来以后要更加耐心加细心的做了。16.最接近的三数之和和上题很像,但有个大区别就是求得是最接近的而不是一个确定的;不过这也不难,维护一个最接近的值就行了。但是看着简单还是不够细心,各种bug,改了n次才最后AC。考虑问题一定要全面,为空时,值为0时。此外,此题的最接近值通过判断来替换,不能通过count=min(count,某某某)来进行,因为这类求距离还有正负的涉及绝对值的很麻烦,老老实实用if吧,if不算丢人,别乱用反而AC不了了。17电话号码的字母组合不会,答案很经典,学会默写,学习这种思路。还是没有耐下心来思考。memorize18。四数之和没什么新的难点,在15题的基础上加一个循环就行了。19.删除链表的倒数第N个节点链表的真正入门,这种类型的题用双指针法。创建两个链表cur和pre,将原链表head赋给这两个,然后第一个先走n步。之后让两个链表同时走,走到第一个走完为止,此时第二个就达到要求了,给他执行pre->next = pre->next->next;删掉当前节点。最初赋值时,cur,pre和head共享相同的内存地址,其实是一个东西,此时三者的任意一个改变均会改变,但是后来cur=cur->next为什么只有cur变化了呢?因为这里又重新赋值了,cur已经跟pre和head是一伙的了!!!接下来的变化只是单纯的赋值,并没有发生删除和替换,只有最后那句pre->next = pre->next->next;才真正执行了一个替换,所以最后返回头结点head就返回了我们想要的结果。20.有效的括号当年考研复试题,考察对栈的理解和使用。但是居然错了又错,没有细心把握。主要错点是stack.top(),不进行为空判断,导致空指针错误。长点心吧,这么简单的题,别浮躁。21.合并两个有序链表简单的方法是用递归法。普通法也行,但没你最初想的那么简单,应该是这样。创建两个临时链表a1,a2等于两个实参l1,l2,再创建一个存储最终结果的链表l3,并用a3临时;每次存储都是a3->next=a1;a1=a1->next;a3=a3->next;这样每次变的是a3,而l3则一直在扩展。22.括号生成当之无愧的难题,需要使用高级递归算法。记住这种方法和所有使用场景,做到一见到相关题就能想到这种方法。虽然是高级递归(回溯法),但本质还是递归,要满足递归的性质,有终止条件,有转移方法。23.合并K个排序链表用优先队列和小顶堆。腾讯的题,难得一批,死死记住。可以用set,自做了!24.两两交换链表中的节点要求一多逻辑就没倒清。而且没想到添加一个虚拟头结点来控制(新技能get)。一目了然的图,不要怕新建的节点多,能解决问题就行。以后遇到链表的题可以画个图好理解25.K个一组翻转链表非常难的题, 纯粹考察链表基础。26.删除排序数组中的重复项灰常简单,一个unique一个distance,注意unique是 c++标准模板库STL中十分实用的函数之一,使用此函数需要#include 头文件该函数的作用是“去除”容器或者数组中相邻元素的重复出现的元素(1) 这里的去除并非真正意义的erase,而是将重复的元素放到容器的末尾,返回值是去重之后的尾地址。 (2) unique针对的是相邻元素,所以对于顺序顺序错乱的数组成员,或者容器成员,需要先进行排序,可以调用std::sort()函数自做:设立一个index,若nums[i]!=nums[i+1] ; nums[index++]=nums[i];最后nums[index]=nums[nums.size()-1]。然后resize。27.移除元素简单,原地接收,不满足要求的continue。同上,且更简单。28.实现strStr()要考虑全各种情况。而且这题不咋样,各种bug29.两数相除比较偏,用简单方法能做出来,就是代码量多,不过这题考察的不是这个,是很偏的知识点。很不舒服这题主要考两点,1)负数的表示方法(最高位为1,减去其余位) 2)使用左移形成2的指数倍,加快运算。使用递归化简运算,最快12ms。意义不大30.串联所有单词的字串绝对的难题,思路是先把words的几个字符串存入unordered_map的字典记录。然后遍历s,核心语句如下 { string t = s.substr(i + jm, m); if (m1.find(t)==m1.end()) break; //没匹配上,j永远等不了n,这个i作废 ++m2[t]; if (m2[t] > m1[t]) break; } if (j == n) res.push_back(i);其实就是先把words们存起来,然后遍历s,一次走words[0]步,再通过find查找是否在子串里,这样来判断。若最后没问题j走到n那就代表匹配上了,存入。31.下一个排列先转化为数学问题,其实就是 从数组倒着查找,找到nums[i] 比nums[i+1]小的时候,就将nums[i]跟nums[i+1]到nums[nums.length - 1]当中找到一个比nums[i]大的元素交换。交换后,再把nums[i+1]到nums[nums.length-1]排序,就ok了32.最长有效括号会者不难。算是简单的难题了。判断如果匹配后栈为空则max=i+1,如果不为空看是否i-c.top()>max,谁大谁就是max。c.top是当前的’('的 i 位置。33.搜索旋转排序数组o(n)时间复杂度很简单,o(lgn)就很难了,不想做了。那会太傻了,搜索+排序这两个字就定位了这道题是O(logn)的二分法。34.在排序数组中查找元素的第一个和最后一个位置因为有O(logn)的要求,所以用二分法,注意二分法是mid+1和mid-1,而不是i++,j–;此外本题的特殊性,应该找到后直接令res[0]=nums[mid],res[1]=nums[mid+1],然后再根据具体情况更改这两个。35.搜索插入位置非常简单,没必要说了36.有效的数独自作出来的,嵌套的有点多,我用的是set自动删除重复元素和原有元素进行判断,如果相同则true。38.报数自作啦!哈哈哈,确实是简单题,虽然做了很长时间。39.组合总和第三次差点自做了,可惜还欠点。但思想是学会了。这类题用回溯法,回溯法的关键是重写一个函数,该函数的参数一般都特别多,而且有个start,end,source,res不停的变这两个start,end指针,主要是source一般不好变,只能通过start和end这两个下标来进行改变回溯,并且不停地存入新的res。本题核心void combinationSumDFS(vector &candidates, int target, int start, vector &out)40.组合总和II在39的基础上,递归函数的i改成i+1,因为不能重复用。41.缺失的第一个正数难题,主要是因为时间和空间要求苛刻。可根据题意用下标法, 遍历一次数组把大于等于1的和小于数组大小的值放到原数组对应位置,然后再遍历一次数组查当前下标是否和值对应,如果不对应那这个下标就是答案,否则遍历完都没出现那么答案就是数组长度加1。42.接雨水太难了,太难了。
1. 找出最高点
2. 分别从两边往最高点遍历:如果下一个数比当前数小,说明可以接到水
43.字符串相乘差点根据小学学的乘法自做了,可惜没通过大数据。看来一点int都不能出现,得纯用string,那这个题确实难了。通过string res(num1.size()num2.size(),0);来接收。每一位计算结果的个位数字存在res[i+j+1],进位存在res[i];最后删除没用的空间的0就行了。44.通配符匹配45.跳跃游戏II好题,可惜没做出来,不过最起码动了脑子。大佬的代码很清晰。维护三个变量,一个当前位置,一个从当前至下一跳的最长距离的位置,该位置等于max(next,i+nums[i]),保证了下一跳的位置肯定是以前所有方法能跳的最远的距离,一个步数。当下一跳位置>=数组长度时,直接返回步数+1,结束。否则每次当循环i走到当前位置时,执行跳转,步数+1,当前位置变为下一跳位置。46.全排列剑指offer27类似,那个是字符串的全排列。很重要,要掌握47.全排列II比46多几行代码,unique去重。不要用set去重,效率低。48.旋转图像不能借助辅助空间,只能在原矩阵上改动。那么肯定不能乱覆盖,不然数据丢失了,那么就应该想到交换swap来做。挺抽象的, 扣了四个边界出来.四个边界点交换. 没遍历一层.就往里缩一个矩阵.49.字母异位词分组核心:用map<vector,vector> list;来存,遍历每个string的每个char,vector tmp的下标为该char-‘a’,每遇到一次就+1.这样,保证了相同数量字母组成的map的key里都是一样的,将这些相同的string添加就行了50.Pow(x,n)同剑指offer12题,简单快速幂。51.N皇后难题。人生第一次和八皇后正面交锋。能把八皇后做出来就不错了。这题一般都是回溯法,回溯法和递归的区别就是回溯会返回之前的几步(如果当前不可行或不满意的话),所以叫 回 溯.http://www.sohu.com/a/224239296_68444552.N皇后II这个只要解法,不用输出,异曲同工。53.最大序列和动态规划典型,dp[i]=max(dp[i-1]+nums[i],nums[i]);max=遍历dp[i]中的最大值。54.螺旋矩阵这题已经做了两道了,这道题是给矩阵序列化,本题有个简单之处是: 将已经走过的地方置0,然后拐弯的时候判断一下是不是已经走过了,如果走过了就计算一下新的方向。55.跳跃游戏非常经典。和45题跳跃游戏II关联pmax记录能走到最远的位置,cur用来更新pmax,最后return pmax>=nums.size()-1;56.合并区间先排个序,然后扫一遍即可58.最后一个单词的长度自做,挺简单,就是考虑各种情况,其中比较恶心的是“a ".末尾有个空格的,要专门从后往前遍历删一趟。59.螺旋矩阵II这题型都做吐了。自做了60.第K个排列在全排列的基础上给出第几个,这是笨办法,好办法是找规律。61.旋转链表前面能看懂,最后两行不知道怎么跟new_head关联上的?62.不同路径普通递归法太慢了,得优化。所以得用一个数组a[m][n]来记录每次递归结果,这样就能不用重复计算,思想和斐波那契那类优化一样。63.不同路径II一开始鄙人以为用回溯法,因为碰到障碍物要退回去,然而还是蠢。由于只有两个方向 向右以及向下,所以这道题不用搜索 我们只需要用 动态规划就能做。状态转移方程:dp[i][j]=a[i][j]1?0:dp[i-1][j]+dp[i][j-1];同时,这个题也要记得用数组保存结果来优化。64.最小路径和前两题基础上的dp,状态转移dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + a[i][j];66.加一简单题,考虑全面即可67.二进制求和不能转化为int,因为会溢出!!!老老实实用string做。这种需要了解哪个参数比较长的,可以写一个函数,这样节省一半代码量和空间。69.x的平方根自做遍历忒慢。还要注意long long。比较快的方法有牛顿迭代法和二分法。70.爬楼梯斐波那契,青蛙跳台阶,填充矩阵。71.简化路径用栈解决,有一个小技巧,在path末尾加一个斜杠,就不用处理path末尾的特殊情况了。不太懂,搁置感觉这题不会问,太偏。 标签:总结,head,遍历,nums,LeetCode1,next,链表,100,dp
来源: https://blog.csdn.net/weixin_43165083/article/details/100595123