LeetCode简单题思路梳理

Median of Two Sorted Arrays

这个问题显然可以用归并排序来处理,但是复杂度是线性的。问题要求给出一个O(log(m + n))的算法,应该是从两序列分别有序这一特性来入手,利用类似二分的方式来完成中位数的查找。

Longest Substring Without Repeating Characters

从每个位置向后遍历256个字符即可,因为ASCII就这么多字符。复杂度可以算作是线性的,但是应该存在更漂亮的做法。
Update: 确实存在更优雅的滑动窗口解法,大意是,先试图移动头指针,没法向前的话就while循环缩短尾部。然后写起来的话是正好相反,在循环体内就直接先收缩尾部然后移动头部。

Add Two Numbers

链表水题,但是可以不用头节点吗?

Longest Palindromic Substring

存在O(n)的算法,但是思路复杂,利用了类似后缀树的性质。朴素想法是从每个位置同时向两边检索,判断是否回文,复杂度O(n^2)。注意需要考虑回文长度分别为奇数和偶数的情况。

ZigZag Conversion

模拟水题。

Reverse Integer

利用sprintf或者sstream可以很轻松地实现。

String to Integer (atoi)

考察思考的仔细程度。判断是否溢出可以直接用long long类型,但是要注意类型转换时是否会出现问题,必要时还是要加上括号。另外,可能存在+-号同时出现的情况。

Palindrome Number

判断数字是否回文,还是用sprintf或者自己实现一个即可。

Regular Expression Matching

实现基本的正则表达式,本质上是一个DP。这个问题还是非常有意思的,为此专门写了一篇博客分析。

Integer to Roman && Roman to Integer

整数转换为罗马数字,理解罗马数字是怎么定义的即可。

Longest Common Prefix

全部遍历一下即可,应该不存在更优的算法了。

3Sum && 3Sum Closest && 4Sum

先前已经总结过,排序后使用头尾指针遍历的方法可以优化掉一层循环的复杂度,对于这类问题已经是最优的算法了。如果要求精确相等,我们可以不考虑当前位置前面的元素,否则就必须要考虑到,这是3Sum与3Sum Closest这两种问题的区别。

Letter Combinations of a Phone Number

直接上Python,可以写出很漂亮的代码。

Remove Nth Node From End of List

依然是链表操作问题,使用快慢指针的思想来处理。具体的做法就是慢指针等待一定时间后再出发。不过需要注意的一点是,程序要判断需要删除的是否是头节点。

Valid Parentheses

用判断括号表达式是否合法。注意虽然是三种括号但是仍然是只用一个栈。

Remove Duplicates from Sorted Array

水题,但是注意输入的数组可能为空!

Merge k Sorted Lists

这题真是卡得爽死我了,应该早点想到的,显然不存在线性算法,比如如果全是只有一个节点的链表,那么就相当于要对这些值排个序,复杂度最低也是O(nlogn)呢。既然可以引入log的复杂度,那么就好想了。一种思路是使用multiset,创建一个结构体或者对象包装一下ListNode *,重载比较运算符,扔进set里面来自动维护有序。每次取的时候选取最小的元素即可,复杂度是O(n*k*logk)。另外一种方式则是用堆(优先队列)来维护有序,本质上是一样的么,但是应该可以缩减掉一个常数。

当然其实更好想的做法是分治,类似归并排序的方式不断合并两个小的链表成为一个大链表,不过看起来占用空间反而会更严重的样子,所以还是不予考虑了(其实是没想到)。

重新考虑一下,其实还是基于堆的算法复杂度最小,可以到O(nlogk)

Swap Nodes in Pairs && Reverse Nodes in k-Group

两道翻转链表的题目,题意是要求将链表k个一组来翻转。是一个实现起来复杂细节比较多的问题。链表翻转本身是一个经典的操作,但是其实这个题目给出的限定条件有点坑。如果不达指定长度的链表节点同样要求翻转的话,其实可以实现一个只需遍历一遍链表即可完成的算法,而且代码也会非常精简。总而言之,这个题目实现起来需要抠很多操作细节。

Remove Element && Implement strStr()

两道直接在网页上敲,盲交然后1A的水题……

Divide Two Integers

要求模拟二进制操作来实现除法。需要注意的是,可能会给出非常极端的数据,直接就溢出了。对待这种情况,最好的办法就是糙快猛地引入long long,用更大值域的类型来处理问题,省心省力!

Substring with Concatenation of All Words

本质上还是在用滑动窗口的思路来处理,设一个窗口的宽度为L.size() * L[0].size(),即为所有words的长度;从S[0]开始滑动,如果当前窗口中有一个word匹配不上,就将窗口移动一个字符,从S[1]继续,以此类推……
需要注意的细节是,输入数据存在重复的word,所以必须检查每个word出现了多少次,在每个窗口中用map记录一下即可搞定。

Search for a Range

二分查找练习。

Search Insert Position

还是STL里的lower_bound。

Valid Sudoku

判断数独是否合法,几个循环套上去即可。

Count and Say

理解题意的话就好办了,意思是在模拟一个人数数字的过程。直接模拟就可以了,似乎题目所给的数据规模不大。

Rotate Image

就是旋转矩阵,新开辟空间的话做法是显然的。如果一定要求In Place即常数空间占用的话,则矩阵必须是方阵,需要用类似一层一层旋转的技巧来完成。参考链接:http://en.wikipedia.org/wiki/In-place_matrix_transposition、http://blog.gssxgss.me/rotate-90-degress-nxn-matrix/

Anagrams

折腾半天没有明白题意,其实这东西叫做“字母易位造词法”,意思是说,给你一系列String,让你给出它们中包含字母相同的所有字符串。思路就很简单了,首先把字符串排序后,用map做哈希,加入到所对应的vector中就可以了。STL大法好!

Pow(x, n)

就是实现快速幂,但是要注意,输入中可能存在负数。而且它的测试case相当严格,会给出一个最小的int32,这个负数是无法转换为32位长度的正数int的。为了省事我直接就改了数据类型为long long(逃)。

N-Queens && N-Queens II

经典的NP八皇后问题,剪枝思路就是尽量利用对角线性质,不用再多说了。第一问需要能够输出所有可行的方案,我的做法是利用全局的字符数组来记录,不知道有没有更为优雅的解决方案。

Maximum Subarray

非常经典的题目,正确的状态表示其实是这样的:记max[i]为前i个元素中,从任意位置起始,到最右端元素结束的连续序列的最大和。这样的话,我们就有状态转移方程:

$$max_{i} = max(max_{i - 1} + a_{i}, a_{i})$$

它的意思是说,在第i个位置,我们要不然将它与前面的连续子序列连接形成更长的子序列,要不然就从它开始一个新的子序列。注意到状态之间只有前后依赖关系,所以并不需要显式开辟数组。但我们需要注意的是,实际要求的是max[i]中的最大值,因此在递推max[i]的过程中,我们需要再维护一下其最大值。

Spiral Matrix && Spiral Matrix II

就是一个模拟,要旋转着来输出矩阵中的每一个元素。这类题目可以通过计算下角标规律的方式来完成,不过更为简洁的做法是,把它看作是模拟行走,每次不能继续向前的话就右转,直到走完该走的步数即可。超出界限的位置、以及先前走过的位置,被认为是不能走的位置。注意处理需要返回空向量的情况,即输入矩阵为空。

Sort List

链表归并排序,核心思路和数组归并排序是一致的。首先使用快慢指针将链表分拆(Partitioning),然后分别对两个链表进行排序(Sort),最后递归合并(Merge)即可。可惜这个合并的递归并非尾递归,所以效率上不是很高,某种意义上而言还是开辟了非常数空间的内存。我们也可以使用不那么漂亮的方式,用具体的链表操作一步一步地合并链表,虽然看起来冗长多了,但是效率会有明显的提升。

Merge Intervals

典型的区间合并问题,使用贪心排序策略来处理。首先按照左端点优先排序,如果左端相同的话就按照右端来排。排序之后,简单考虑一下合并策略即可。对于其后那道Insert Interval,我们可以直接复用此题的代码来完成。当然这个题目也可以把代码写成线性复杂度的,不过也就意味着还要做一些特殊情况的处理和判断,个人觉得意义不大。

Rotate List

模拟链表移位。如果输入数据k保证不超过链表长度的话,那么这个问题可以用快慢指针一次遍历链表完成。否则的话,我们必须事先知道链表的长度,求模之后才能够进行处理。

Unique Paths && Unique Paths II

非常显然的DP类型问题,子问题的划分也是很简单的。状态转移方程:dp[i][j] = dp[i][j - 1] + dp[i - 1][j]。对于存在障碍物的情况,直接在DP的时候进行判断即可。

Minimum Path Sum

状态转移方程:dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]

Add Binary && Plus One

N进制字符串相加,显然的模拟水题。先直接相加,然后按位调整即可。反正最多只会增加一位。

Path Sum && Path Sum II

裸的DFS搜索,注意边界条件的情况判定。

Text Justification

巨麻烦的字符串处理问题,题意也很难理解,本质上应该源自word之类文字处理软件的行排版算法。总而言之,一点一点抠边界细节就行了。

Sqrt(x)

实现整数的开方运算,其实就是二分查找法。注意,对于开方后出现整型截断的情况,得到的答案平方值会小于被开方数。这种情形下,需要记录先前一个合法的小于值,其实就是类似lower_bound的那种简便处理方法。

Climbing Stairs

DP水题,状态转移方程:dp[i] = dp[i - 1] + dp[i - 2]。正好是斐波那契数列。

Simplify Path

对UNIX风格的路径进行化简,模拟水题。需要注意一个小地方,就是如果试图访问根路径/的上一层目录,这是一个合法的访问不会出错,只是会一直停留在根路径而已,因为root的父亲就是它自己。

Edit Distance

经典的DP问题,状态转移方程分为两种状态:如果word1[i] == word2[j],则直接有dp[i][j] = dp[i - 1][j - 1];否则,那么当前状态可以由三种情况转移过来:min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1,分别对应着替换字符、插入字符、删除字符的情况。注意初始化条件:dp[0][i] = dp[i][0] = i,其对应的含义是显然的。

Set Matrix Zeroes

水题一枚,用set记录并去重即可。或者也可以用标记的方式来写。

Search a 2D Matrix I && II

这个题纯粹就是二分查找的二维版本,核心在于行与行之间的关系是单调递增的。我们首先在某一行里面做lower_bound查找,如果找到了,直接返回true,否则根据查找结束时指针的位置,按照二分的思想再去查找对应的行即可。

第二问更有意思一些,首先容易想到类似二分查找的递归,复杂度为\(T(n)=3T(\frac{n}{4})+O(1)\),所以利用主方法解出复杂度为\(O(n^{log_43})\),其中n为矩阵大小。但这个算法虽然优美但并非最优。实际上它有一个\(O(m+n)\)的算法,只需要从右上角开始移动指针,如果当前值大于目标,指针左移;小于目标,指针往下移动即可。正确性容易证明。

Sort Colors

就是计数排序的简化版本。

Combinations && Subsets && Subsets II

裸的DFS问题,注意终止条件的区别,以及如何去重。
对于包含重复元素的Subsets问题,为了有效地去除重复,我们在遍历元素的时候必须保证当前元素已经与前一个不相等,此时才能够继续DFS下去。否则,DFS得到的子集就必然会包含重复的集合。简而言之,就是允许在不同层的递归中DFS一段连续的元素,但是不能在同一层的递归中去连续DFS元素,因为这样必将导致出现多次与先前状态完全等价的情形。

Word Search

典型的DFS搜索,注意使用标记数组,防止重复走到同一个位置上面。

Word Search II

DFS搜索加字典树剪枝,还是要注意边界条件。

Remove Duplicates from Sorted Array II

把状态转移想明白就可以,分支条件不复杂,O(n)的算法。

Remove Duplicates from Sorted List

基础的链表操作问题,细节需要考虑清楚,画图非常有助于理解。

Remove Duplicates from Sorted List II

这个链表操作就相当繁琐了,实现思路的核心是在大循环内部,使用两重while循环去除掉所有重复的元素。总而言之,编码思路必须清晰。

Maximal Rectangle

最大子矩阵和的变体,也算是最大连续子序列的一种应用吧,压缩掉了一个维度的复杂度。这里的失败判定条件不是出现和为负,而是子矩阵的某一行没有被1所填满,其实问题是变得更简单了。

看了一下程序耗时发现明显偏慢,于是搜了一下网上的题解,发现这题被LeetCode降低数据规模了。实际上可以把算法优化到O(n^2),方法是首先从每一行起,将矩阵转化为直方图,然后套用上一题的算法,直接O(n)算出最大矩形,即为答案。“必须填满矩阵”这个特性被用来巧妙地优化了算法,这俩题目放在一起着实用心良苦……

Construct Binary Tree from Preorder and Inorder Traversal

经典问题,从先序遍历和中序遍历的结果构造出原本的树。还是要把握二叉树的递归性质,既然是递归遍历出的结果,自然能够递归求解。核心在于,先序遍历结果的第一个元素一定是root,于是在中序遍历中找到这个root元素,将序列拆分后递归左右子树即可。需要注意的是,不要算错下角标,最好采用左闭右开的区间表示法。

Construct Binary Tree from Inorder and Postorder Traversal

完全同上,只不过是由中序遍历和后序遍历来重新构造出树。还是利用一样的性质,后序遍历的序列中,最后一个元素是根元素。
一个需要注意的小地方是递归终点的写法,由于输入的树可能是空树,因此代码中不应该假设其中至少有一个元素

Partition List

考察使用O(1)的空间复杂度就地划分链表。其实由于链表的特殊性质,很多在它上面的操作并不需要额外的空间,比如经典的归并排序。因此我们设置两个指针,分别指向大于等于给定x的链表,以及小于x的链表,然后遍历一遍输入链表,将每个节点分别连接到这两个链表之一中即可。最后,注意合并链表的时候,一定要对情况分类讨论!不然容易忽略边界细节。

Merge Sorted Array

考察数组的就地归并排序,核心思想是首先在数组尾部预留出空间,然后倒着遍历一遍来进行操作即可。可以写出非常简短漂亮的代码。

Gray Code

相对无聊的找规律问题,核心在于正确推导格雷码的生成规律。看起来LeetCode这个OJ没有Special Judge,因为题目说只能对一组固定的特解进行Judge,略渣。

Decode Ways

斐波那契数列的扩展问题,由于输入数据中存在0,因此需要考虑到一些边界细节,比如存在前导零的情况。总而言之,还是利用传统的斐波那契递推,只不过递推条件发生了些许改变,需要剪掉那些不合法不能继续推的状态。

Reverse Linked List II

反转链表中指定的一段,还是一个考察细节的链表操作题目,在纸上模拟好流程,敲成代码就行了。大体就是先找到起始位置,然后一边反转一边走到终点位置,最后分情况讨论,把它们重新连接好。这题终于能裸敲1A了……

Restore IP Addresses

依然是DFS问题,尝试搜索将IP地址拆分为四段,然后判断是否可行即可。问题在于一些边界细节的处理,比如前导零

Interleaving String

遇到这种类型的字符串问题,很容易想到其解法应该就是DP。使用dp[i][j]表示s1的前i个字符和s2的前j个字符是否能够组成s3中的前i+j个字符,状态转移方程就是:dp[i][j]=(dp[i-1][j]&&s1[i-1]==s3[i+j-1])||(dp[i][j-1]&&s2[j-1]==s3[i+j-1]),初始化条件也就显然可得了,但是要注意边界细节。

Validate Binary Search Tree

判断一个二叉搜索树是否合法。按照定义,中序遍历BST一定可以得到一个非降序列。但是这题的数据非常严格,所以需要注意实现方法。此时就不能在求取子树中最值的时候使用一个有限的INF值了,而是应该设置好完善的判断条件。

Symmetric Tree

判断一棵二叉树在结构上是否呈对称状态,还是递归处理。这个题目仅存的难点应该就在于各种边界细节了,难得的能1A的题目。

Binary Tree Level Order Traversal I && II

分层遍历二叉树,也是那种没什么难度的题目,一遍BFS就可以了。注意用pair的话不要手残写出编译错误!
第二道题目要求反序输出遍历结果,所以就是输出前反转一下即可。感觉这样做并不是很优雅,但是理论上应该也不会存在一遍就能完成的做法了吧?如果返回值要求是List的话,是可以做到不反转的,因为这种数据结构在头部增加元素的时候是O(1)的复杂度,DFS或者BFS的时候反着往里面添加就可以了;但是C++的代码要求返回vector<vector<int>>,这种情况下就不行了。

Binary Tree Zigzag Level Order Traversal

承接上题思路,注意到只有深度为奇数的层需要被反转,于是在输出前简单处理一下即可。STL里面的reverse函数可以轻易完成这个操作。

Convert Sorted Array to Binary Search Tree

题意没说清楚,但其实是超水的数组转成平衡二叉搜索树。我就按照正常的左闭右开的区间表示方法来写了,虽然不知道判题系统是不是有SPJ功能,但总之是1A了。

Convert Sorted List to Binary Search Tree

唯一技巧性的地方在于快慢指针的使用,可以把代码实现变得非常简洁优美。其实链表跟数组一样可以使用左闭右开的区间表示法,但是取中间位置的时候就不得不遍历一遍了。使用快慢指针,终止条件是while(p!=r&&p->next!=r)。而初始状态下,则应该有:TreeNode *l=head, *r=NULL;

Balanced Binary Tree

判断一颗二叉树是否是平衡的,定义是:左右子树的最大高度差不超过1,且左右子树均是平衡二叉树。还是毫无疑问的利用递归子结构性质,没有难度就不多说了。

Minimum Depth of Binary Tree

求二叉树的最小深度,这里的“深度”定义为从根节点到叶子节点的节点数。非常烦人的题目,主要是在考察对题意的理解,以及边界细节的处理。总而言之,此时判定递归终点就不能再简单地看指针是否为NULL了,而是要确认找到叶子节点。

Valid Palindrome

判断是否是有效回文,水题一枚,直接用Python写函数式了。

Pascal's Triangle I && II

第一问是个水题,就是输出杨辉三角,找规律即可。
第二问要求直接输出对应行,利用二项式定理来算就行,不占用额外的空间复杂度。但是注意,计算组合数的代码段,只适用于中间值C(n,k)*k不超过整型范围的情况。在这个题目中不能保证这一点,因此不得不开long long

Triangle

这个DP实在是太经典了,不可能有人不会吧……

Sum Root to Leaf Numbers

依然是树上的DFS问题,但是要稍微小心一下递归逻辑,有点容易写错。

Surrounded Regions

这题就比较坑了,首先它在栈空间上面做了限制,没法用最爱的DFS来完成;其次,即便是BFS来写,也会吃一发MLE。事实上这个问题属于经典的思路反转:我们要求标记掉所有不贴边的连通区域,那么必然会导致很多时候搜索完之后发现这块区域贴边了,于是该次搜索作废。当然我们可以采用某些记忆化的手段来进行优化,保证这样的失败只会出现一次,只是这样会稍微繁琐些。换个思路来看,如果我们从四个边缘上开始搜索,包含O的连通区域都会被标记掉;对于其他位置,直接置为X即可。这样的话,我们就能够以非常简洁的算法完成任务。

Clone Graph

其实就是BFS算法克隆一个图。注意这个图是无向有环且有自环的,因此需要我们在新建节点的时候加以注意:如果这个节点已经新建过了,那么直接取它的指针拿来用;如果确实还没有建立过,才会new一个新的出来。另外就是注意BFS的搜索顺序以及标记顺序的问题。

Gas Station

最大连续子段和的又一个变体问题。考虑两个基本性质:
1. 如果所有的gas加在一起还不能满足消耗,那么一定无法完成一圈;
2. 如果上述条件不成立,那么必然只存在两种情况:从位置0开始即可完成一圈;或者存在一个位置i,在区间[0, i)内,消耗大于油量,在区间[i, n)内,消耗小于油量。
因此,问题转换为我们需要找到一个最长子段[0, i),使得它的和为负值。在这个最长的位置之后,剩下的一段[i, n)就可以保证一定可以走完,并且走完之后的剩余油量还足够再走完这段油耗大于油量的区间[0, i)从而回到起始点i

Reverse Linked List && Reorder List

Reverse list仅仅是简单的就地反转链表,这种题目显然没什么可说的,几行就够了。对于第二题,要求按照一种特定的顺序来重排序链表,这里只需要意识到,这种结构稍微复杂的就地重排,不可能仅仅遍历一遍就完成(递归算法好像可以,但本质上并不是常数空间复杂度的)。所以实际上只需要把链表截断,将后半段反转,然后就地归并即可,最终时间复杂度O(n),空间复杂度O(1)

Insertion Sort List

链表插入排序,基本思路跟在数组上的插入排序类似。不同点在于,如果当前节点并不满足有序性,需要移动位置时,就需要先把它从链表中移除,找到合适的插入位置以后,再O(1)进行插入。注意指针操作时,正确地维护链表头部即可。

Evaluate Reverse Polish Notation

就是用栈对逆波兰式求值而已,需要仔细考虑一些细节的边界条件,题目本身没有难度。

Intersection of Two Linked Lists

又是快慢指针的应用:先分别从两头出发,测量整条链表的长度;然后链表长的一端的指针先出发,短的一端后出发,一定会在分叉点处相遇。

Maximum Product Subarray

源自于Maximum Subarray的经典线性递推思路,只不过维护的状态多了一个而已。因为最大值的来源可能有两种:最大值与当前正值的乘积,或者最小值与当前负值的乘积,因此状态转移方程有:

$$ \begin{cases} max_{i} = max(max_{i - 1} * a_{i}, min_{i - 1} * a_{i}, a_{i}) \\ min_{i} = min(max_{i - 1} * a_{i}, min_{i - 1} * a_{i}, a_{i}) \end{cases} $$

同样需要注意,这里在递推的时候,可以避免显式开辟数组,但是由于是两个方程,所以存在一点点的依赖关系,即计算min的时候要依赖max,所以在更新max前需要先将其备份。此外,状态max[i]min[i]的含义还是类似,即“从任意位置开始到结尾的连续序列的最大(小)积”,因此,我们需要在计算过程中维护一个最大值,才是我们所需要的答案。

Find Peak Element

题目要求复杂度是O(logn),因此必然是利用二分思想来处理。这里需要把二分时的判断条件想清楚,为此,我们需要分析这个数列的性质。考虑到其左右两端均为负无穷,因此数列中必然存在至少一个波峰,以及零个或者多个波谷,此时在脑海中大致能够描述其形状了。我们直接对数组进行二分,则二分到的中间位置mid必然只有四种情况:增加、减少、波峰、波谷。如果是波峰,则直接返回答案即可;如果是波谷,说明左侧或者右侧区间必然存在一个波峰,选择其一进行递归即可;如果是单增,说明右侧必然存在波峰;如果是单减,说明左侧必然存在波峰。至此,有了二分的判断条件,再加上合适的边界条件,就可以得到答案了。

Compare Version Numbers

这个就是水题了,只不过需要列出俩边界case而已:比如1.0.01.0应该是相等的,但是1.1.1要大于1.1

Fraction to Recurring Decimal

需要注意繁琐细节的除法模拟题,注意到循环小数的出现是有规律的:当同样的除数和余数pari出现的时候,说明已经完成了一次循环节。因此,以此pair进行哈希即可。这题主要需要注意细节。

Excel Sheet Column Title

稍微复杂一点的进制转换问题,核心思路是找规律。我们注意到A在其中并不是代表着十进制0的含义,因此,在转换过程中,为了避免余数为零导致进位,我们需要在能够整除的情况下,将数值减1。

Excel Sheet Column Number

上一道题目的反转版本,这时候就非常简单了。

Majority Element I && II

经典的摩尔投票算法及其扩展,用于在O(n)的复杂度内确定数组中占1/n多数的元素,其中n可取2、3、4,……这个算法的核心思想是这样:占1/n多数的元素最多存在n-1种,对于这最多n-1种元素,即便剩余的所有元素与之抵消,依然能够保证它们会有剩余。因此,我们只需预留n-1个位置,跑一遍正常的投票流程,那么占1/n多数的n-1种元素一定会留在这n-1个位置上面。但是这个算法本质上只保证必要性,所以我们还需要再遍历一遍来确保充分性。

Factorial Trailing Zeroes

数学题。这里利用到这样一个性质,阶乘末尾零的个数,取决于n个数总共提供了多少个(2, 5)配对,其乘积就会作为最后的末尾零。但是,我们考虑到在阶乘的过程中,2出现的次数要远远多于5出现的次数,因此,实际上我们只需要求出1~n中总共有多少个5的约数即可,这个统计过程可以在log(n)复杂度内完成:

$$ result = \frac{n}{5^{1}} + \frac{n}{5^{2}} + \frac{n}{5^{3}} + ... + \frac{n}{5^{k}} (5^{k} <= n) $$

Binary Search Tree Iterator

实际上就是求二叉搜索树的后继,只不过这颗树没有带parent指针,而是需要自己用栈维护而已。还是需要牢记二叉搜索树的定义啊。

Largest Number

贪心算法,只要排序处理就可以了。考虑最优的排列下,我们如果任意调换一对数字的排序,必然导致组合出的结果变小。因此,这个最优排列必然是有序的。这样的话,我们只需要定义一个排序函数,两个数字比较时,比较的结果是两者两种组合方式的较大者,这样就能够保证最终结果的有序性质了。

Repeated DNA Sequences

注意到DNA序列的长度为10,因此显然存在取巧的办法。事实上,这个长度的特定序列完全可以塞进一个int里面进行哈希判重了,所以很容易可以解决。另外注意,保存答案的时候也需要注意去重。

Rotate Array

考察数组的就地移位,这里有两种实现思路。第一种是,直接将前后两段反转,然后再把整段一起反转回来。第二种是复杂的就地移位,边界条件比较难以考虑。总而言之,尽量使用第一种思路,非常好写。

Valid Perfect Square

标准的二分,应该不会有低于O(logn)的算法了。注意,这题同样有一个数学规律,即1 + 3 + 5 + ... + 2n - 1 = n ^ 2。当然,用这个规律的算法,复杂度反而要高一些,只是一个结论罢了。

House Robber

一维DP问题,记状态f[i]为一定包含第i个元素的最大值,g[i]为一定不包含第i个元素的最大值,则有状态转移方程:

$$ \begin{cases} f_{i} = g_{i - 1} + a_{i} \\ g_{i} = max(g_{i - 1}, f_{i - 1}) \end{cases} $$


由于状态之间依赖关系简单,因此不需要显式存储,直接迭代更新并记录最大值即可。

Binary Tree Right Side View

题意有点难以理解,其实是要求给出二叉树最右侧的那些节点的值。这样的话我们只需要BFS一遍二叉树,把每个节点的深度信息传递一下,利用其分层遍历的性质,把每一层最右侧相应的值存储到相应的位置即可。

Number of Islands

无脑BFS打标记即可,这种题目简直没有难度可言。

Bitwise AND of Numbers Range

一道找规律的位运算题目,这道题要求我们将一个区间内的所有数字按位做and操作。我们只需要注意到一个规律,运算结果其实就是这个范围内所有数字的公共前缀,此时问题就转化成求这样一个公共前缀。这里有一个非常精妙的算法:我们只需要枚举一个初始状态下所有bit均为1的一个mask,直至maskmnand结果相等,如果不相等就将mask左移,这样我们就找出了这个公共前缀,也就是我们所要的答案了。总而言之,这是一个非常精妙的位运算技巧。

Happy Number

哈希打表。

Count Primes

筛法求素数。应该不会有低复杂度的做法。

Isomorphic Strings

同样是哈希,注意一定要对称做映射,然后比较的时候是要先映射之后再比较。

Course Schedule I && II

拓扑排序判断图中是否有环,套路题。

Implement Trie (Prefix Tree)

字典树,直接敲就行,注意理解前缀的含义。

Minimum Size Subarray Sum

又是经典的滑动窗口,一定要想清楚头尾指针怎么移动,不然会挂corner case。

Add and Search Word - Data structure design

字典树上的广搜,注意一些边角处理。广搜容易写错,貌似深搜比较好写的样子。

Kth Largest Element in an Array

经典的区间第k大,注意选择哨兵是随机选取的,但需要对序列做特殊检查,如果整个区间元素都一样,就应该直接返回,否则会陷入无限递归爆栈。

Kth Smallest Element in a BST

BST树上第k大,跟区间是一样的原理,但是利用树的递归性质非常好写。另外存在一个常数优化,不过代码不是很直观。

Product of Array Except Self

双端遍历,利用输出数组存储状态,就可以不占用额外的空间。

Lowest Common Ancestor of a Binary Tree

这题既然没有保证树的有序性,那么肯定是要DFS一遍了。还是以递归方式来处理,两个目标节点可能具有几种情况:同时在左子树or右子树,或者是一个在根节点一个在左子树,以及一个在根节点一个在右子树。以正确的方式来记录答案即可。

comments powered by Disqus
Published:
2014-12-31
Last modified:
2016-09-02
分类:
Tag:
TOC
LeetCode简单题思路梳理
  1. Median of Two Sorted Arrays
  2. Longest Substring Without Repeating Characters
  3. Add Two Numbers
  4. Longest Palindromic Substring
  5. ZigZag Conversion
  6. Reverse Integer
  7. String to Integer (atoi)
  8. Palindrome Number
  9. Regular Expression Matching
  10. Integer to Roman && Roman to Integer
  11. Longest Common Prefix
  12. 3Sum && 3Sum Closest && 4Sum
  13. Letter Combinations of a Phone Number
  14. Remove Nth Node From End of List
  15. Valid Parentheses
  16. Remove Duplicates from Sorted Array
  17. Merge k Sorted Lists
  18. Swap Nodes in Pairs && Reverse Nodes in k-Group
  19. Remove Element && Implement strStr()
  20. Divide Two Integers
  21. Substring with Concatenation of All Words
  22. Search for a Range
  23. Search Insert Position
  24. Valid Sudoku
  25. Count and Say
  26. Rotate Image
  27. Anagrams
  28. Pow(x, n)
  29. N-Queens && N-Queens II
  30. Maximum Subarray
  31. Spiral Matrix && Spiral Matrix II
  32. Sort List
  33. Merge Intervals
  34. Rotate List
  35. Unique Paths && Unique Paths II
  36. Minimum Path Sum
  37. Add Binary && Plus One
  38. Path Sum && Path Sum II
  39. Text Justification
  40. Sqrt(x)
  41. Climbing Stairs
  42. Simplify Path
  43. Edit Distance
  44. Set Matrix Zeroes
  45. Search a 2D Matrix I && II
  46. Sort Colors
  47. Combinations && Subsets && Subsets II
  48. Word Search
  49. Word Search II
  50. Remove Duplicates from Sorted Array II
  51. Remove Duplicates from Sorted List
  52. Remove Duplicates from Sorted List II
  53. Maximal Rectangle
  54. Construct Binary Tree from Preorder and Inorder Traversal
  55. Construct Binary Tree from Inorder and Postorder Traversal
  56. Partition List
  57. Merge Sorted Array
  58. Gray Code
  59. Decode Ways
  60. Reverse Linked List II
  61. Restore IP Addresses
  62. Interleaving String
  63. Validate Binary Search Tree
  64. Symmetric Tree
  65. Binary Tree Level Order Traversal I && II
  66. Binary Tree Zigzag Level Order Traversal
  67. Convert Sorted Array to Binary Search Tree
  68. Convert Sorted List to Binary Search Tree
  69. Balanced Binary Tree
  70. Minimum Depth of Binary Tree
  71. Valid Palindrome
  72. Pascal's Triangle I && II
  73. Triangle
  74. Sum Root to Leaf Numbers
  75. Surrounded Regions
  76. Clone Graph
  77. Gas Station
  78. Reverse Linked List && Reorder List
  79. Insertion Sort List
  80. Evaluate Reverse Polish Notation
  81. Intersection of Two Linked Lists
  82. Maximum Product Subarray
  83. Find Peak Element
  84. Compare Version Numbers
  85. Fraction to Recurring Decimal
  86. Excel Sheet Column Title
  87. Excel Sheet Column Number
  88. Majority Element I && II
  89. Factorial Trailing Zeroes
  90. Binary Search Tree Iterator
  91. Largest Number
  92. Repeated DNA Sequences
  93. Rotate Array
  94. Valid Perfect Square
  95. House Robber
  96. Binary Tree Right Side View
  97. Number of Islands
  98. Bitwise AND of Numbers Range
  99. Happy Number
  100. Count Primes
  101. Isomorphic Strings
  102. Course Schedule I && II
  103. Implement Trie (Prefix Tree)
  104. Minimum Size Subarray Sum
  105. Add and Search Word - Data structure design
  106. Kth Largest Element in an Array
  107. Kth Smallest Element in a BST
  108. Product of Array Except Self
  109. Lowest Common Ancestor of a Binary Tree
  110. Comments