技巧篇算法小白如何快速且高效的刷力扣题?
算法萌新在刷力扣时,虽然已有一些算法基础但仍然出现一题都做不出来的现象,经常有以下困惑:
1。代码写了又删、删了又写,写到一半才发现逻辑走不通,没有整体思路。
2。不能分析出题目需要用到什么算法。
那么想要高效地刷题首先要知道每道题目应该怎么做。
刷题小技巧
力扣上的Easy题分两种,一种是不需要算法逻辑,只要按照题目描述将代码敲出来即可。另一种是不拐弯抹角,只需要一种标准算法即可。
对于第一类Easy题,我们可以先根据题意将逻辑理顺,写出伪代码,然后逐步补全小段的代码。
例如:1029。两地调度
题目描述很清晰,看完题目我们可以先想出代码的流程:funtwoCitySchedCost(costs:ArrayIntArray):Int{先让所有人都选择距离最近的城市判断到A城市的人数和到B城市的人数相差的数量计算从当前人数多的城市移动到另一座城市的最小差值将差值排序,让差值小的人移动到另一座城市}
将流程写出来后,再逐步补全代码:classSolution{varresult0varcountA0varcountB0valdistancemutableListOfInt()funtwoCitySchedCost(costs:ArrayIntArray):Int{先让所有人都选择距离最近的城市chooseShortestCity(costs)判断到A城市的人数和到B城市的人数相差的数量if(countAcountB)returnresult计算从当前人数多的城市移动到另一座城市的最小差值calculateMoveDistance(costs)将差值排序,让差值小的人移动到另一座城市shortDistanceMoveToAnother()returnresult}privatefunchooseShortestCity(costs:ArrayIntArray){costs。forEach{if(it〔0〕it〔1〕){resultit〔0〕countA}else{resultit〔1〕countB}}}privatefuncalculateMoveDistance(costs:ArrayIntArray){costs。forEach{if(countAcountB){if(it〔0〕it〔1〕){这是现在往A城市走的人,记录他们往B城市走的距离distance。add(it〔1〕it〔0〕)}}else{if(it〔0〕it〔1〕){这是现在往B城市走的人,记录他们往A城市走的距离distance。add(it〔0〕it〔1〕)}}}}privatefunshortDistanceMoveToAnother(){distance。sort()resultdistance。take(Math。abs(countAcountB)2)。sum()}}
这就好比先规划好目的地,再一小段一小段的达成目标。开发者要写一个功能单一的函数是比较简单的,这就像一个小步快跑的过程。
再比如:1030。距离顺序排列矩阵单元格
同样的,我们先根据题目描述写出代码流程:funallCellsDistOrder(R:Int,C:Int,r0:Int,c0:Int):ArrayIntArray{计算每个点到(r0,c0)的距离,保存在map中(距离坐标列表)由于R和C的范围都是0100,所以map的key范围是0200,从0到200输出map即可}
然后逐步补全代码classSolution{valdistanceMapHashMapInt,MutableListIntArray()valresultmutableListOfIntArray()funallCellsDistOrder(R:Int,C:Int,r0:Int,c0:Int):ArrayIntArray{计算每个点到(r0,c0)的距离,保存在map中(距离坐标列表)calculateDistance(R,C,r0,c0)由于R和C的范围都是0100,所以map的key范围是0200,从0到200输出map即可recordResult()returnresult。toTypedArray()}privatefuncalculateDistance(R:Int,C:Int,r0:Int,c0:Int){for(xin0untilR){for(yin0untilC){valdistanceMath。abs(xr0)Math。abs(yc0)distanceMap〔distance〕?:let{distanceMap〔distance〕mutableListOf()}distanceMap〔distance〕!!。add(intArrayOf(x,y))}}}privatefunrecordResult(){for(iin0。。200){if(distanceMap。isEmpty())breakif(!distanceMap。containsKey(i))continueresult。addAll(distanceMap〔i〕!!)}}}
伪代码可以帮助我们明确自己需要做什么,这一点和测试驱动开发(TDD)的思想是一致的。我们在做的时候就会知道:只要我完成了这些步骤,就可以实现我要的效果。而不是盲目的写了又删、删了又写。
判断条件
新手在阅读算法题目时,经常会分析不出题目需要用哪一种算法来解决。主要的原因在于阅读的题目太少,对每一类算法适用的场景不熟悉。
这个问题很好解决,力扣对每个题目都设置有相应的tag,我们可以按照力扣对题型的分类,先看同一类题目。对这类算法题目的描述就会有个比较清晰的认识。如同学习英语一样,看多了倒装句,一眼就能看出倒装句的结构;看多了感叹句,一眼就能看出感叹句的结构。这也是一个熟能生巧的过程,需要花时间去了解、总结每类算法的特点,量变引起质变。
例如,我们点击二分查找标签,显示如下:
粗略浏览,可以发现,二分查找的题目中很多都带有有序、排序字样。接下来我们进一步分析题目,先从简单题开始:
35。搜索插入位置
这道题就属于上文所说的只需要一种标准算法的Easy题。没有任何的拐弯抹角,一道典型的二分题目。题目场景有以下特点:
数组是有序的
答案是有界的,在〔0,nums。size〕之间
目标值越大,答案越大,这里有一个单调递增的关系
二分法有标准的套路(本例使用的Kotlin语言,不过算法跟编程语言关系不大):二分法套路fundichotomy(){。。。左边界varleft右边界varright记录答案varanswhile(leftright){中间值valmiddle(leftright)2猜测是否满足条件if(guess(middle,。。。)){如果满足条件,记录答案ansmiddle缩小搜索范围,在更小的值中搜索rightmiddle1}else{不满足条件,缩小搜索范围,在更大的值中搜索leftmiddle1}}returnans}猜测是否满足条件funguess(middle,。。。){。。。}
如果你熟悉这个套路的话,我们可以很快写出答案:classSolution{funsearchInsert(nums:IntArray,target:Int):Int{varleft0varrightnums。sizewhile(leftright){valmiddle(leftright)2if(nums〔middle〕target)returnmiddleif(nums〔middle〕target){leftmiddle1}else{rightmiddle}}returnleft}}
再来一道中等题目练练手:
33。搜索旋转排序数组
分析题目,可知:
数组旋转之前是有序的
答案是有界的,在〔0,nums。size〕之间
目标值越大,答案越大,这里有一个单调递增的关系
与标准的二分题目相比,只多了一个条件:有序数组被旋转了。只要先将数组旋转回来,再用二分法求出结果,再根据旋转的位置调整下标即可。其实中等题目很多都是在典型的算法题上面拐一个弯,困难题目一般是糅合两种算法,万变不离其宗。此题解决方案如下:classSolution{funsearch(nums:IntArray,target:Int):Int{valrotateIndexgetRotateIndex(nums)valorderedNumsgetOrderedNums(nums,rotateIndex)varleft0varrightorderedNums。sizewhile(leftright){valmiddle(leftright)2when{orderedNums〔middle〕targetrightorderedNums〔middle〕targetleft找到了答案,根据旋转下标调整结果elsereturnwhen{rotateIndex0middleorderedNums。sizerotateIndexmiddlemiddleorderedNums。sizerotateIndexelsemiddlerotateIndex}}}return1}获取旋转前排好序的数组privatefungetOrderedNums(nums:IntArray,rotateIndex:Int):MutableListInt{valorderedNumsmutableListOfInt()orderedNums。addAll(nums。drop(rotateIndex))orderedNums。addAll(nums。take(rotateIndex))returnorderedNums}获取旋转位置的下标privatefungetRotateIndex(nums:IntArray):Int{for((index,value)innums。dropLast(1)。withIndex()){if(valuenums〔index1〕){returnindex1}}return0}}
通过以上两个题,相信你对二分法已经有了一个基本认识,他们都有一个共同点:答案是有界且单调的。事实上,只要是满足此条件的题目都可以使用二分法解决。所以我们分析题目时,可以通过检查这一条件来判断是否是二分算法的题目。
接下来我们再来分析一下动态规划题目的特点,选择动态规划标签:
粗略浏览,可以发现,题目中很多带有最大最小、最长最短、不同字样。上文已经说到,简单的题目往往更典型,我们以一道简单题目为例:
70。爬楼梯
这是一道典型的动态规划题目。
分析题目:假如我们要到达第100步台阶。我们不可能一步登天,直接飞上去。我们只能从第98步走两步到达,或者从99步走一步到达。
同样,我们也不可能直接飞到98步,我们只能从96步走两步到达,或者从97步走一步到达。
如果用f(n)表示到达第n步的走法,根据我们的分析,以下方程成立:
f(n)f(n2)f(n1)
边界条件如下:
f(1)1f(2)2
所以我们可以写出以下算法:classSolution{funclimbStairs(n:Int):Int{if(n1)return1if(n2)return2returnclimbStairs(n2)climbStairs(n1)}}
然而这样是不能通过的,因为递归会带来大量的重复计算,所以我们将其修改为迭代:classSolution{funclimbStairs(n:Int):Int{valfIntArray(n1)for(iin1。。n){when(i){1f〔i〕12f〔i〕2elsef〔i〕f〔i1〕f〔i2〕}}returnf〔n〕}}
这样写这道题目就AC了。这道题很好的体现了动态规划算法题目的特点:问题能够以大化小,化到最小时有边界条件。
对于动态规划题目,我们只需要处理以大化小的过程和边界条件即可。这个以大化小的过程,专业术语叫状态转移。状态转移的条件被称作状态转移方程,本题中,状态转移方程就是:f(n)f(n2)f(n1)。只要找出了状态转移方程,就可以很轻松的解决动态规划问题,这就是动态规划算法的套路。
此题还可以再次优化,仔细分析题目可知,我们并不需要记录f(i)的每一个值,只需要记录前两个值即可。也就是只记录到达前一步阶梯的方法总数和到达前两步阶梯的方法总数,进一步优化的算法如下:classSolution{funclimbStairs(n:Int):Int{前面第二个数varlastTwo0前一个数varlastOne1repeat(n){lastOnelastTwolastTwolastOnelastTwo}returnlastOne}}
对于每一个题目,不要仅仅满足于通过。最好是不断地优化代码,使得其时间复杂度和空间复杂度最低。养成这个好习惯,以后一出手就是最优方案,有助于我们的编程水平进一步提升。
写在最后
刚开始刷题时,如果厌倦了从Easy入手的话,就按照开头所讲的先写伪代码,再逐步补全的方式编程,可以让你少走很多弯路。此外,还可以去力扣题解区看看别的小伙伴的解题思路,开拓解题思维。
做算法题时循序渐进,不要上来就做困难题目,Easy题反而更加典型,不掺杂其他算法在其中,更有助于萌新理解此类算法。每个题目多刷几遍,不断地优化代码,直到脑中对此题有一个清晰的认识,切忌萌混过关。
学习算法要渐次进行,先掌握一类算法,钻研透了再去掌握另一类。建议先定一个能达到的小目标,比方说我先掌握一个算法。
BY
本文作者:力扣
声明:本文归力扣版权所有,如需转载请联系。
两弹元勋功臣邓稼先功臣邓稼先于1924年出生在安徽省怀宁县。在北平上小学和中学以后,于1945年自昆明西南联大毕业。高才生邓稼先1948年到1950年于美国普渡大学(PurdueUn……
古代人为什么挖井后在井里放一只乌龟呢?在很多历史记载中,我们发现古人每次挖井都需要在井里放一只乌龟。为什么?很多人对为什么要在井里放一只乌龟感到好奇,并展开了一系列猜测。有人猜测乌龟是一种非常有灵性的动物,寿……
拿破仑败于锡扣是一个谣言?道理有些可笑法军很穷用不起1812年拿破仑进攻俄国大败,重要原因是在寒冬使用了锡制纽扣?近些年来,这个说法几乎成了历史定论,甚至被当成鸡汤式的教育素材,一遍遍证明细节决定成败或不重视化学的后果。然……
清朝审转复核制度的特点来源:智慧普法平台陈静王琼娴审转复核制度作为清朝司法制度的重要组成部分,在司法实务操作中发挥了无可替代的作用,特别是其精密的设计,展现出统治者对地方司法权的控制以及……
复合集流体锂电池新材料再现细分赛道近年来,我国新能源汽车在政策、市场、技术等因素的影响下销量持续增长。据中汽车协发布的数据,2022年国内新能源汽车全年销量为688。7万辆,占汽车新车销量25。6。锂电池是目前……
若我到了560岁,一定远离烫卷发,显老!不如这3种,洋气减龄烫卷发对于中年女性来说似乎有种不可抵挡的魔力,许多女性人到中年之后都会选择去做一次烫卷发。但是烫卷发所带来的影响,你有了解过吗?损伤发质是一回事,更重要的是,它会让我们看起来比……
今天,纪念先驱李大钊他是中国最早的马克思主义传播者中国共产党的主要创始人之一他鼓励中国青年以青春之我创建青春之国家青春之民族1927年李大钊被反动军阀杀害临刑时……
华为作为5G领导者,却只能卖4G手机就是个笑话,你怎么看?如何评价余承东说华为作为5G领导者却只能卖4G手机就是个笑话?可能对于华为来说,最无奈的事情,就是这个了吧,在5G建设上是遥遥领先的,在芯片领域也是最早进入5GSOC的手……
坚持统一战线作者:尹俊(北京大学国家发展研究院研究员)党的十九届六中全会通过的《中共中央关于党的百年奋斗重大成就和历史经验的决议》强调:建立最广泛的统一战线,是党克敌制胜的重要法宝,……
助人为乐梦中获褒奖清朝时期,福建有一位林孝廉(举人的雅称,即通过乡试的读书人),北上进京参加会试。他乘坐的船,停靠在吴江的一座高楼下。一天夜里,楼上起火,众人急着救火,喧哗一片。忽然,一个少妇穿……
明日立冬,无论贫富,记得吃5种美食,驱寒滋补又暖身,平安入冬明日立冬,无论贫富,记得多吃这5种美食,驱寒滋补,平安入冬。秋去冬来,又是一年冬来到,立冬是二十四节气之一,是冬天的第1个节气,立冬的到来意味着寒冷的季节即将拉开序幕,此……
三国志周瑜传周瑜字公瑾,庐江舒人也。初孙坚兴义兵讨董卓徙家于舒坚子策与瑜同年独相友善瑜推道南大宅以舍策升堂拜母有无通共。瑜从父尚为丹杨太守,瑜往省之。会策将东渡,到历阳,驰书报瑜,瑜将兵迎……