前言

我第一次频繁打开Leetcode还得追溯到三年前的华为时光,因为专业级考试的压力,不得不深夜死扣算法。o(╥﹏╥)o。

刷得多了以后,感觉算法就是程序员世界的每一个小的场景缩影,或者说是抽象的实践,对每一个问题场景,从解决问题,到优化解决方案,每一行代码的修改,都能很直接的感受到收益,因此慢慢喜欢上了刷算法的感觉。不过在华为时期,由于高强度的工作压力,让我没有更多的时间去拥抱LC。目前换了工作,每天上午9:30上班,下午6:30下班,回到家修整好,8点打开电脑,有大把的个人时间,所以可以进行高强度的刷题啦!!!(*^▽^*)。

初入江湖:被 LeetCode 虐得体无完肤

还记得初次邂逅 LeetCode,那感觉就像被卷入了一个神秘莫测的江湖,而我只是个手无寸铁、毫无经验的小虾米。面对一道道题目,我就像丈二和尚摸不着头脑,完全不知所措。

就拿简单的数组求和问题来说吧,这本该是编程世界里的 “入门小菜”,可对当时的我来说,却如同难以翻越的大山。我对着题目干瞪眼,脑袋里一片空白,根本不知道从哪里开始下手。好不容易憋出了几行代码,满心期待能顺利通过,结果提交后,错误提示像雪花般纷纷扬扬地飘来,什么数组越界啦,类型不匹配啦,看得我眼花缭乱,头皮发麻。更尴尬的是,连最基本的边界条件都被我忘得一干二净,比如当数组为空时该如何处理,我压根就没考虑到。

还有那个经典的 “两数之和” 问题,给定一个整数数组和一个目标值,要求找出数组中两个数的和等于目标值的索引。我当时的做法简单粗暴,直接用了两层循环,暴力地对每两个数进行组合判断。代码写得又长又复杂,时间复杂度高得吓人,空间复杂度也没控制好。结果呢,提交后虽然能通过部分测试用例,但一遇到稍微大一点的数据规模,就直接超时,根本通不过。那时候,看着别人简洁高效的代码,再看看自己写的这堆 “垃圾”,心里别提多沮丧了,感觉自己和那些大神之间的差距简直比银河系还宽。

艰难摸索:踏上打怪成长之路

在被 LeetCode 狠狠打击之后,我内心燃起了一股不服输的劲儿,暗暗发誓一定要征服这个 “恶魔”。我深知,想要在这片江湖中立足,就必须得掌握一些真本事,于是,我开启了疯狂的学习模式。

我一头扎进了题解的海洋,疯狂地看各种题解,试图从别人的思路中找到解题的灵感。每一道题,我都会仔细研究多种解法,分析它们的优缺点,学习其中的技巧和思路。同时,我也意识到,数据结构和算法是解题的核心,于是我开始系统地学习各种数据结构和算法知识。

我从最基础的数组、链表开始学起,了解它们的特性、操作方法以及适用场景。为了搞清楚链表的原理,我画了无数张示意图,模拟链表的插入、删除、查找等操作,直到这些操作在我脑海中形成了清晰的流程。接着,我又学习了栈、队列、二叉树等数据结构,尤其是二叉树,它的递归遍历方式让我着实费了一番脑筋。我反复练习前序遍历、中序遍历、后序遍历的代码实现,通过不断地调试和思考,终于掌握了其中的奥秘。

算法方面,我学习了冒泡排序、选择排序、插入排序等基础排序算法,以及二分查找、深度优先搜索(DFS)、广度优先搜索(BFS)等常用算法。还记得学习二分查找时,我总是在边界条件的处理上出错,不是 left 和 right 的更新条件写错,就是 mid 的计算有误。但我没有放弃,通过不断地查阅资料、参考别人的代码,我逐渐理解了二分查找的精髓,能够熟练地运用它来解决各种查找问题。

每天,我都逼着自己去理解至少一道难题。有时候,一道题我可能会花费几个小时甚至一整天的时间去思考、去尝试。遇到困难时,我也想过放弃,但是一想到自己的目标,我就又咬咬牙坚持了下来。在这个过程中,我逐渐积累了一些经验,也掌握了一些解题的技巧。比如,遇到数组问题时,我会先考虑能否使用双指针法;遇到图论问题时,DFS 和 BFS 是常用的解题思路;遇到动态规划问题,关键是要找到状态转移方程。

虽然这个过程充满了艰辛和挑战,但每一次成功解出一道题,那种成就感简直无法用言语来形容。就像是在游戏中成功打败了一个强大的怪物,获得了珍贵的装备和经验值,让我更加有信心和动力去挑战下一道题。

小有所成:初见曙光

功夫不负有心人,经过一段时间的疯狂刷题和学习,我终于迎来了一丝曙光,在 LeetCode 的江湖中,我也算是小有所成啦!

现在,我已经能够独立解决一些中等难度的题目,那种感觉就像是从一个只能在新手村晃悠的小菜鸟,终于有了闯荡江湖的底气。就拿动态规划类的题目来说吧,以前看到这类题目,我就像看到了洪水猛兽,吓得只想转身就跑。可现在,我已经掌握了一些应对它们的诀窍。

比如 “不同路径” 这道题,一个机器人位于一个 m x n 网格的左上角,每次只能向下或者向右移动一步,问总共有多少条不同的路径到达右下角。刚看到这题时,我也是一脸懵,但通过不断地思考和尝试,我发现这道题可以用动态规划来解决。我定义了一个二维数组 dp,其中 dp[i][j] 表示到达第 i 行第 j 列的不同路径数。然后,根据动态规划的思想,我得到了状态转移方程:dp[i][j] = dp [i-1][j] + dp [i][j-1] 。有了这个方程,再结合边界条件,我成功地写出了代码,当看到提交通过的那一刻,我激动得差点跳起来。

还有 “跳跃游戏”,给定一个非负整数数组 nums ,每个元素代表在该位置可以跳跃的最大长度,判断是否能够到达最后一个下标。我从后往前思考,用一个变量记录当前能够到达最后一个下标的最左位置,通过不断更新这个位置,最终判断是否能够从起始位置到达最后一个下标。虽然这个过程花费了我不少时间,但当我成功解出这道题时,那种成就感简直爆棚。

现在,我不仅能写出正确的代码,还能分析出代码的时间和空间复杂度。以前,我根本不理解这些复杂度有什么用,只觉得能把题做出来就行了。但现在我明白了,分析复杂度可以帮助我优化代码,让代码更加高效。比如在一些排序算法中,我可以根据不同的场景选择时间复杂度更低的算法,从而提高程序的运行效率。

渐入佳境:思路如泉涌

如今,我在 LeetCode 的刷题之旅可谓是渐入佳境,那种感觉就像是打通了任督二脉,解题思路如泉水般源源不断地涌出。现在,只要一看到题目,我的脑袋就像一台高速运转的超级计算机,瞬间就能判断出这道题的类型,是搜索、动态规划还是贪心算法等等,并且能立刻在脑海中勾勒出解题的框架。

就拿前几天遇到的一道关于字符串匹配的题目来说吧,题目是这样的:给定一个主字符串和一个模式字符串,要求在主字符串中找出模式字符串第一次出现的位置,如果不存在则返回 -1。刚看到这题时,我的第一反应就是用 KMP 算法来解决。因为我对 KMP 算法已经非常熟悉了,它的核心思想就是利用已经匹配的部分信息来减少不必要的比较,从而提高匹配效率。

于是,我立刻在脑海中回忆起 KMP 算法的框架。首先,要构建部分匹配表(也称为前缀表),用于记录模式串中每个位置的最长公共前后缀长度。这个前缀表可是 KMP 算法的关键所在,它就像是一把神奇的钥匙,能够帮助我们在匹配失败时快速找到下一个比较的位置,避免了从头开始的重复比较。我熟练地写出了构建前缀表的代码,通过一个循环,不断地比较模式串中当前字符和前缀末尾字符是否相等,来更新前缀表的值。

构建好前缀表后,就进入了匹配的过程。我定义了两个指针,一个指向主字符串,一个指向模式字符串,然后开始逐个字符进行比较。如果当前字符匹配,就将两个指针都向后移动一位;如果不匹配,就根据前缀表的值,将模式字符串的指针移动到合适的位置,继续进行比较。在这个过程中,我充分利用了前缀表的信息,大大减少了不必要的比较次数,整个匹配过程就像行云流水一样顺畅。

最终,我成功地写出了代码,并且一次就提交通过了。那一刻,我心里别提多得意了,感觉自己就像一个武功高强的大侠,轻松地战胜了一个又一个的难题。通过这道题,我也深刻地体会到了,对各种算法和数据结构的熟练掌握是多么的重要,只有这样,才能在遇到不同类型的题目时,迅速找到解题的思路和方法。

除了快速判断题目类型和运用相应的解题框架,我现在还特别注重代码的优化。每写完一段代码,我都会仔细思考是否还有更高效的解法,是否可以减少时间复杂度和空间复杂度。比如在一些涉及数组遍历的题目中,我会尝试使用双指针法或者滑动窗口法,来降低时间复杂度;在一些需要存储中间结果的题目中,我会考虑使用哈希表或者动态规划的方法,来优化空间复杂度。这种不断追求代码优化的过程,不仅让我的代码更加高效,也让我对算法和数据结构的理解更加深入。

总结与展望:继续前行

回顾这段在 LeetCode 上的刷题历程,真的是感慨万千。从最初的懵懂无知、被题目虐得体无完肤,到现在能够熟练地解决各种难题,这个过程中充满了汗水与坚持,但也收获了满满的成长与进步。

坚持每日一题真的是一个非常好的习惯,它就像一场每天都要进行的战斗,让我始终保持着对编程的热情和敏锐度。在这 112 天的连续打卡中,我从未想过放弃,哪怕有时候工作很忙,或者遇到了特别难的题目,我都会想尽办法抽出时间来完成当天的任务。因为我知道,一旦中断,就很容易失去这种坚持的动力。这种坚持不仅让我的编程能力得到了提升,更培养了我的毅力和自律能力,让我在面对生活中的其他困难时,也能保持积极的心态去克服。

善于总结归纳也是我在刷题过程中积累的重要经验。每完成一道题,我都会仔细回顾自己的解题思路,分析其中的优点和不足之处。对于那些做错的题目,我会将它们整理到一个错题本上,详细地记录错误原因和正确的解法。每隔一段时间,我就会拿出错题本进行复习,通过反复练习,确保自己不会再犯同样的错误。同时,我还会将相似类型的题目进行归类,总结出它们的通用解题方法和技巧。这样,在遇到新的题目时,我就能迅速地联想到相关的解题思路,提高解题效率。

在这里,我也想鼓励其他的开发者们,不要害怕在 LeetCode 上挑战自己,不要被那些看似困难的题目吓倒。只要你坚持下去,不断地学习和总结,你一定会发现自己的编程能力在不知不觉中得到了巨大的提升。刷题不仅仅是为了应对面试,更是一个提升自我、挑战自我的过程,它能让我们在编程的道路上走得更远、更稳。

未来,我还会继续在 LeetCode 的江湖中闯荡,挑战更多更难的题目。我相信,还有无数的精彩等着我去发现,还有更高的山峰等着我去攀登。我会带着这份对编程的热爱和执着,不断提升自己的编程能力,在代码的世界里创造出更多的可能!

补充:

同步一下目前的刷题进展:

  • 全栈刷题总数:894道。

  • 每日一题持续打卡:112天。