leetcode解题思路

收录https://oj.leetcode.com/problems/ 所有题目的解题思路。


Min Stack

题目要求:

设计一个最小栈,要求能实现pop、push、min操作,也就是返回栈内元素最小值。

解法:

新加一个辅助栈,保存当前栈内最小值。当入栈元素小于辅助栈栈顶元素时,同时入辅助栈;出栈时,当出栈元素等于栈顶元素时,同时辅助栈出栈。


Find Minimum in Rotated Sorted Array II

题目要求:

旋转过的有序数组,数组内可能有重复元素,求最小值。

解法:

1、比较num[begin] < num[end],试探是否旋转了。如果没旋转,直接返回num[begin];

2、如果旋转了。分情况讨论:

a、num[mid] > num[begin],说明最小值在后半段[mid+1, end];

b、num[mid] < num[begin],说明最小值在前半段[begin, mid];

c、num[mid] == num[begin],如果num[begin] == num[end],则两半段都要找一遍,如果num[begin] > num[end],则最小值在后半段[mid+1, end];


Find Minimum in Rotated Sorted Array

题目要求:

旋转的有序数组,数组内没有重复元素,求最小值。

解法:

1、比较num[begin] < num[end],试探是否旋转了。如果没旋转,直接返回num[begin];

2、如果旋转了。分情况讨论:

a、num[begin] < num[mid];说明最小值在后半段[mid+1, end];

b、num[begin] > num[mid];说明最小值在前半段[begin, mid];

c、num[begin] == num[mid];说明begin == mid,直接返回min(num[begin], num[end]);


Maximum Product Subarray

题目要求:

寻找连续子串中乘积最大的数。

解法:

和求最大字段和解法相似,不同的是,这里保留一个最大值,一个最小值。每次扫描一个数字时,更新这两个变量。


Reverse Words in a String

题目要求:

翻转整个字符串,但不颠倒单词内部字符的顺序。

解法:

先整体翻转一下,再以空格为分隔符翻转每个单词。


Evaluate Reverse Polish Notation

题目要求:

求解反转波兰表达式。

解法:

栈操作即可。遇到数字入栈,遇到运算符则出栈,每次出栈两个数字,计算出的结果后入栈运算结果。


Max Points on a Line

题目要求:

给出一堆点,求在同一条线上最多的点个数。

解法:

用unordered_map保存斜率,选取某点为参照物,计算其他点与该点的连线的斜率。平行于Y轴的斜率可用INT_MAX表示。注意重复点。


Sort List

题目要求:

用nlogn的时间复杂度,对单链表排序。

解法:

把链表拆成两半,分别排序后,再合并。


Insertion Sort List

题目要求:

用插入排序 排序单链表。

解法:

逐步遍历,逐一插入。


LRU Cache

题目要求:

设计一个最近最少使用的cache,支持set、get操作。

解法:

设计一个双链表,保存kv数据,以满足快速移动节点操作。同时设计一个unordered_map方便根据key快速查找链接节点node。


Binary Tree Postorder Traversal

题目要求:

非递归实现二叉树后续遍历。

解法:

设置一个栈,先找到最左边的节点,经过的所有节点入栈。然后开始出栈,出栈时输出节点,并且判断该节点是否是新的栈尾节点的左节点。如果是,则寻找新的栈尾节点的最左边的子节点,寻找过程中经过的节点入栈。其他情况则继续出栈。直到栈为空。


Binary Tree Preorder Traversal

题目要求:

非递归实现二叉树前序遍历。

解法:

设置一个栈,先找栈最左边的节点,经过的节点逐一入栈,同时输出。然后开始出栈,出栈时判断是否存在右节点,如果存在,找到最左边的节点,并且逐一入栈和输出。如果不存在,则直接出栈。直到栈为空。和后续遍历相比,这里访问过的节点直接出栈,并不做保存。


Reorder List

题目要求:

将链表按照even-odd重新排序。要求in-place。

解法:

用快慢指针找到中间节点,将链表一分为二(要将前半段尾节点left_tail->next = nullptr),后半段逆序后,再和前半段逐一合并。


Linked List Cycle II

题目要求:

给定单链表,找到环的开始节点,如果不存在环,则返回null。

解法:

快慢两指针同时指向头结点,一个走一步,一个走两步,能相遇说明有环,遇到null说明无环。相遇后,将慢指针重新指向头结点,两个指针同时一次一步开始前进,相遇的那个点就是环的入口。


Linked List Cycle

题目要求:

给定单链表,确定该链表是否有环。

解法:

同上。


Word Break II

题目要求:

给定一个字符串,可以由词典中某些单词按顺序组成,要求输出所有可能的组合。

解法:

DP+backtracking。可以用回溯法解决,从头扫描字符,当发现字典中存在的字符串时,将剩下的字符串继续考察,如果直到字符串结束,则算作一个组合。否则,抛弃。

但这种方法存在重复查找字典的操作。所以用dp的方法,申请一个二维数组flag,flag[i][j]表示从i到j的子串是否在字典中存在。扫描一遍字符串发现所有可能匹配的子串,然后从尾部开始,检测是否能够通过该尾部组合,成功匹配。比如:flag[i]行全部flag均为false,说明从i开始往后找,不可能找到匹配的子串,所以flag[X][i]的所有flag均可以置为false。得到这个二维数组后,再用backtracking的方法可以节约匹配时间。


Word Break

题目要求:

给定一个字符串,判断是否可以由词典中某些单词按顺序组成,返回true or false。

解法:

可以用word break II的思路,dp+backtracking。也可以用hash_map代替dp数组,设置一个map,保存无法由词典构造的所有字符串。


Copy List with Random Pointer

题目要求:

单链表,每个节点带随机指针指向别的节点。要求copy一份一模一样的单链表。

解法:

插入法。分三步走,首先将新节点放在每个旧节点的next上面。其次,将新节点 的random指向旧节点的random的next(如果random非空)。最后,解除新旧节点的链接关系(注意bound case,结尾时要判断next是否为空)。


Single Number II

题目要求:

一个整数数组,除了一个整数出现一次,其他整数均出现三次,求出现一次的这个整数。

解法:

用数组vec[32]统计所有数字,在各个比特位上出现的次数。每个比特位上的数字mod3后剩下的就是出现一次的整数。(注意有负数的可能,所以全程都直接统计比特位)


Single Number

题目要求:

一个整数数组,除了一个整数出现一次,其他均出现两次,求这个出现一次的整数。

解法:

用上题的方法也可以。这里用异或也行。


Candy

题目要求:

给一组孩子分糖果,要求每个孩子最少一个糖果,且高个孩子比相邻的矮个孩子分到的糖果要多。求,最少要提供多少糖果可以满足要求。

解法:

初始化每个孩子一个糖果。从左往右扫描一遍,后面的孩子和前面的孩子做比较,如果后面的孩子个头大,但糖果却不多,则调整至比前面的孩子多一个。从右往左扫描,同理。然后把所有的糖果加起来。


Gas Station

题目要求:

一个环上面分布着几个加油站,加油站里有油,问车子从哪个加油站开始出发,可以跑完全程。

解法:

从某点开始,记录所有油量-消耗量,当发现不够时,从下一个加油站开始继续统计。同时记录所有经过的加油站的油量-消耗量。

当环走完一圈时,如所有油量-消耗量>0,则可以跑完全程,且起点是最后一个记录的可以跑到终点的加油站。


Clone Graph

题目要求:

深度复制一个无向图。

解法:

因为节点label唯一,借助map,记录新图的相应节点。


Palindrome Partitioning II

题目要求:

给定一个字符串s,要求切成的每一块都是回文,求最少需要切几下。

解法:

dp,设置一个初始化数组flag[i][j],表示从i到j的子串是否为回文。if (s[i] == s[j] && (i - j == 1 || flag[i + 1][j-1] == true)) { flag[i][j] = true;}

随后利用这个flag数组,再做一次dp,申请一个一维数组cut[i],记录从0到i需要的切次数,cut[i] =  flag[0][i] == true ? 0 : from 0 to i ,min(cut[j] + 1)


Palindrome Partitioning

题目要求:

给定字符串s,要求切成的每一块子串都是回文,求出所有可能的切法组合。

解法:

dp,设置一个初始化二维数组flag[i][j],表示从i到j的子串是否为回文。

随后,做一次清洗,从头到尾check一遍,如果从i出发所有的子串都不是回文,那么所有到达i的回文都不可能组成s,所以,一旦整个flag[i][x]都是false,那么flag[x][i-1]也应设成false。

最后用backtracking,统计所有可能的组合。


Surrounded Regions

题目要求:

一个矩形,里面有O有X,要求把所有被X包围的O改成X。

解法:

bfs或dfs都可以。在边上的O,肯定是没完全被X包围的。通过四条边,用dfs(递归)或bfs(queue),找到没被X完全包围的O,标成E。然后再扫描一遍矩阵,将O、X全部改成X,E改成O


Sum Root to Leaf Numbers

题目要求:

二叉树,每个节点上有个数字,从跟到叶子的路径可以组成一个数字,求所有数字的总和。

解法:

用bfs递归,找到叶子节点时把组成的数字累加,最后返回总和。


Longest Consecutive Sequence

题目要求:

给定一个数组,要求找到数组中数值连续的最大长度,要求时间复杂度O(n)。

解法:

排序不行,因为排序O(nlgn)。这里用unordered_set存储所有数字,扫描一遍数组,每个数字,挨个找左右数字,更新连续数值的长度。为了防止重复扫描,删除set中已经扫描过的数字。


Word Ladder II

题目要求:

给一个start字符串,end字符串,和一堆词典。要求找到最短的转移方法。有几种列几种。

解法:

bsf(queue),设置前驱map,保存节点前驱,方便恢复路径。


Word Ladder

题目要求:

给一个start字符串,end字符串,和一堆词典。要求找到最短的转移方法。有几种列几种。

解法:

bfs(queue),每遍历完节点记得删除,避免重复计算。


题目要求:
判断是否为回文,可跳过非字母数字字符,且不考虑大小写。
解法:
遇到非字母数字直接跳过。


Binary Tree Maximum Path Sum

题目要求:

二叉树,节点带值,找到值和最大的路径,路径可以从任意节点到任意节点。

解法:

递归。每次返回该节点为根的最大路径和穿过该节点的最长路径。


题目要求:
有一组股票价格序列,最多两次交易,求最大利润。(卖完可以立马买)
解法:
dp问题。先用left[i]数组,求出在i时刻以前(包括i时刻)先买后卖的最大利润。再用right[i]数组,求出在时刻j以后,先买后卖的最大利润。
然后遍历一遍两个数组,求出max(left[i] + right[i])

题目要求:
给定一个股票价格序列,交易次数无限制,求可以获得的最大利润。
解法:
贪心。高点卖,低点买。从左开始扫描,把所有上升坡段的增值累加起来就行。profit += prices[i + 1] > prices[i] ? : prices[i+1] - prices[i] : 0;

题目要求:
给定一个股票队列序列,只交易一次,求可获得的最大利润。
解法:
dp,保存i以前的最小价格,profit = max(profit, prices[i] - min_price)

题目要求:
三角形数组,求从跟到叶子和最小路径。
解法:
dp,申请一个底边长大小的数组,每次数值等于上一行两个数字最小的那个+当前数字,不断更新这个数组即可。(注意每次更新数组,从后往前更新)

题目要求:
三角数组,每个数字等于上一行两个数字之和,求第t行数组。
解法:
dp,申请一个t大小的数组,逐行更新,每次更新从后往前,循环t次。

题目要求:
生成共t行的三角数组,每个数字都是上一行两个数字之和。
解法:
逐行相加即可。

题目要求:
给定二叉树,为每个节点的同行下一节点指针赋值。
解法:
层次遍历,queue。同level节点队尾设nullptr。

题目要求:
给定完美二叉树,同上。
解法:
同上。

题目:
给定字符串S,字符串T,求S中有多少个不同的子序列是T。(子序列,不要求序列连续,但要求顺序)
解法:
dp,开辟二维数组flag[i][j],记录从0到i的S,可以组成多少个从0到j的T,flag[i][j] = flag[i-1][j] + S[i] == T[j] ? flag[i-1][j-1] : 0
最后返回flag[S.size()][T.size()]

题目要求:
将二叉树按前序遍历排成一个单链表。
解法:
非常巧妙的办法,如左子树不存在,则直接看右节点;若存在,则找到左子树的尾节点,也就是左子树的最右边节点,把右子树挂到这下面,然后把左子树移到右边,左指针置空。然后看右指针。这种办法每次都解决一个节点的flatten问题,依次解决直到最后节点。

题目要求:
给定二叉树,找到路径和为某数的路径。
解法:
递归,找到叶子节点,如果和为指定某数,则输出。注意节点可能有正有负,所以不能剪枝,必须遍历所有路径。


题目要求:
给定二叉树和一个给定数字,判断是否存在和为该数字的路径。
解法:
同上。


题目要求:
求二叉树的最小深度。(深度定义为路径上节点的个数)
解法:
递归或非递归(层次遍历用queue)


题目要求:
给定一个二叉树,判断是否是平衡二叉树。
解法:
递归,先判断子树是否为平衡二叉树,并根据返回的子树树高判断本节点的树是否为平衡二叉树。


题目:
用排序好的链表创建BST。
解法:
计算链表长度,以后每次递归到中间节点,用中间节点建根(偶数时,取前一个),递归下去建树。


题目:
用排序好的数组创建BST
解法:
每次用中间节点建树,递归。

题目:
按层遍历二叉树。
解法:
非递归用queue

题目:
中序后序建二叉树。
解法:
递归

题目:
前序中序建立二叉树。
解法:
递归

题目:
求二叉树的最深深度。
解法:
递归。


题目:
层次打印二叉树,要求第一层从左到右,第二层从右到左,以此类推。
解法:
非递归,用queue,每层节点用vector先存着,level%2 == 0时,正序输出,否则,反序即可。


题目:
层次遍历二叉树。
解法:
非递归,用queue。


题目:
给定一个二叉树,判断树是否是对称结构。
解法:
递归,每次判断节点相对应的节点在另一半子树那里,是否存在且相等。


题目:
判断两棵树,是否结构和值相同。
解法:
递归,先看本节点取值是否相同,再看左子树和右子树。


题目:
一个BST,其中有两个节点的值被互换了,请恢复这个BST。
解法:
中序遍历BST,记录pre,如果pre->val > cur->val,说明有被互换,记录下这个pre和cur。如果又遇到了pre->val > cur->val的情况,更新后面那个cur,说明第二个被换的节点在这。如果没遇到,说明前面那对pre和cur就是被互换的节点。找到这两个节点后,直接把值换回来即可。


题目:
验证是否为合格的BST。
解法:
中序遍历,检查pre->val < cur->val,看是否有序。或者,直接递归判断,要求每个节点都在父节点的限定的数值范围内(但这种方法无法处理INT_MIN INT_MAX的情况)。


题目:
判断字符串s是否能由两子串t1、t2交叉组成。
解法:
dp,设置一个二维数组,array[i][j]表示是否能由ti[0....i] t2[0....j]组成。回溯法也能做,但重复计算过多,效率低,会超时。


题目:
给定数字n,返回所有从1.......n可以组成多少BST
解法:
递归解决。每次取一个节点做根,左子树m种和右子树n种都递归生成,然后合并成m*n种。


题目要求:
给定数字n,求1.....n可以组成多少组不同的BST。
解法:
Dp,申请一个数组,每个i,可以由j , 1  i-j-1组成。于是vec[i] = vec[j] * vec[i-j-1]  j从0.....i-1

题目要求:
二叉树非递归中序遍历。
解法:
用栈,先走到最左边,然后出栈,访问,然后再看是否有右子树,有的话,走到右子树的最左边,走的时候入栈。没有的话,继续出栈,访问。

题目要求:
给一串字符数字,要求返回所有可能的ip地址组合。
解法:
回溯法,每次选取一部分ip string,然后剩下的继续选取,直到所有的字符串都被划分完毕。如果步长为4,且所有的字符均被选取完,则保存起来。

题目要求:
从n到m的节点,逆序。
解法:
保存n节点前一个节点,然后开始逆转,直到逆转到m+1节点时终止,这时把逆序链表头部和尾部全部连接好。注意bound case

题目:
给定一个数组,可能有重复数字,要求返回所有子集,子集与子集不能重复,子集内部允许有重复数字,子集内部有序排列。
解法:
典型的回溯问题。背包问题。先排序,每次选取一个,或者不选。如果遇到重复,直接选取重复值从0.....dup_num


题目要求:
A->1 B->2 ..... Z->26,给定一个字符串数字,判断可以组合出多少种组合。
解法:
dp问题,申请一个数组,或者不用数组也行,只需保留前两个组合数目。array[i]表示从0到i可以组成的组合数目。
array[i] = array[i - 1] (if s[i] is valid) + array[i - 2] (if s[i-1, i] is valid) 
字符串长度小于3时,需要单独计算。

题目:
格雷码,(一种编码方法,定义自行百度),给定数字n,求n位格雷码可以表示的数字,按十进制输出。
解法:
格雷码要求相邻的两码,只有一个比特位不同。
可以用栈的方式来做。当n == 1,插入0,1。  当n == 2,从栈尾向栈头获取元素,元素高位前加1,然后入栈。比如n==i 时,入栈元素为 stk.back() | 1 << i。依此类推。

题目:
将有序数组B合并到有序数组A中。
解法:
既然A数组空间够大,那就申请两指针,分别指向A和B有序数组的尾部,从后向前。胜出的就保存在A数组的最尾部。

题目:
google当年的面试题。一个字符串可由树构成,交换左右节点,可形成新的字符串。这两个字符串互为scramble string。
现在给定两字符串,判断是否为scrambled string。
解法:
DP,三维数组,第一维表示长度,第二维、第三维表示从str1的i,str2的j开始。 array[k][i][j]表示:str1从i开始,str2从j开始,长度为k的字符串是否互为scrambled string。

题目:
给定一个数字target,将单链表重整为,所有大于数字target的均排在所有小于数字target的节点之后。
解法:
申请两dummy节点,一个用来挂大于数字target的节点,一个用来挂小于数字target的节点。遍历一边后,将链表分成了大小两个,然后将两个串一起。

题目:
给定一个矩阵,里面有0,有1.求出所有都是1的最大子矩阵的面积area。
解法:
DP。申请一个vector,长度就是二维矩阵的长,vector记录着以当前行结尾连续1的高。从0行到n-1行,循环更新这个vector,如果遇到0,则vector[i]=0;否则vector[i]++。
比如:某个vector可能为{1,2,3,1,0,0,2,1,3}
每次循环中,单独计算最大矩阵,这时求最大矩阵面积的问题就变成了求最大直方图的矩阵面积了。也就是下面这个题目的解法。


题目:
给定一个vector,里面存着整数,表示直方图的长,求最大的矩阵面积。
解法:
用栈。

题目要求:
删除有序单链表中重复的节点。有重复的就全部删掉。
解法:
逐一遍历单链表,如果下一节点为空或不等于下一节点,则过,如果不是,则一直找到结尾或者不等于该节点的节点,直接跳过。
重复节点可能从头结点就有,为了操作方便,可以虚构一个dummy节点。


题目:
删除有序单链表中重复的节点,有重复的只保留一个。
解法:
逐一扫描单链表。和下一个节点做比较,如果相同,则跳过。为了操作方便,也可以虚构一个dummy节点。


题目:
旋转有序数组,允许重复,查找某数字是否存在。
解法:
变种二分查找。分情况:
1、如果 A[begin] < target < A[middle],说明target在左半边。
2、如果A[middle] < target < A[end],说明target在右半边。
3、其他情况均无法判断在左半边还是在右半边,所以直接判断A[begin] == target ,是则返回,否则begin++


题目:
删除有序数组中的重复元素,同一重复的元素最多只保留两个。
解法:
一个写地址指针(从第三个元素开始),一个读指针,每次判断是否同时和写指针之前的两个数相同,如果相同,直接跳过,如果不同,则写入。


题目:
给一个二维字符矩阵,判断给定的字符串是否可以由该矩阵生成。
解法:
回溯法,从二维矩阵的每一个字符都找一遍。同时设置used_flag保证已在路径中的节点不被重复访问。
方便上下左右移动,可以的定义数组move[4][2] =  {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};


题目:
给定一个数组,没有重复数字。返回所有的子集。要求子集有序、子集间不重复。
解法:
回溯法。背包问题,每次选一个数字或者不选,同时step+1,直到step == s.size()则保存结果并返回。


题目:
给定n,k,返回包含k个数字的1.....n的子集。
解法:
同上背包问题。每次选或者不选,同时step +1,只不过退出条件变成了 combine.size() == k


题目:
给定字符串S,目标串T,找到在S中最短的字符串,使得刚好所有T的元素都至少出现了在T中出现的次数。
解法:
用一个hash_map保存T中的元素和次数。
设立两个指针,一头一尾表示选中的子串。用临时hash_map保留子串元素和次数,如果不达T的标准,则右指针向右,如果达标,则左指针向右,直到不达标。
依次类推,每次记录达标的子串和长度。


题目:
经典荷兰旗问题,数组中存在三种元素0,1,2.排列该数组,顺序为0<1<2。要求时间复杂度为O(n)。
解法:
三指针方法。左指针指向数组头,表示0的位置;右指针指向数组尾,表示2的位置。
另一个指针(中指针)开始i遍历数组。发现1,则不管。发现0,则和左指针的元素互换,左指针右移一位。发现2,则和右指针的元素互换,右指针左移一位。直到中指针元素为1,中指针右移。
循环下去,直到中指针和右指针相遇。


题目:
二维数组,每行内部有序,行与行之间有序,下一行的所有值大于上一行。
解法:
先找到所在行,二分。再找到所在列,二分。注意bound case,想做到bug-free,最好先用case自测。


题目:
有一个二维数组,如果某个节点为0,则把节点所在行和所在列所有元素设为0. 要求原地。
解法:
用一个数组记录所有要设置为0的行,再用一个数组记录所有要设置为0的列。
还有一种办法,用两个flag,一个flag记录第0行是否全为0,一个flag记录第0列是需要全为0.这种空间复杂度是常数。
遍历整个矩阵,如果该行为0,只要把matrix[i][0] = 0, 如果需要把该列设置为0,只要把matrix[0][j] = 0. 再遍历一遍,把行列该设置为0的设置为0.最后根据两个flag将0行、0列根据需要设为0


题目:
求两个string的编辑距离。
解法:
经典DP问题。distance[i][j] = min{   distance[i -1][j] + 1,    distance[i][j-1] + 1,     distance[i-1][j-1]  + str1[i] == str2[j] ? 0 : 1  }


题目:
简化文件路径。
解法:
用栈,先将路径按/切分,遇到.时不作为,遇到..时出战,遇到字符时入栈。最后根据栈内结果返回。


题目:梯子一次可以爬一层,一次可以跑两层。求问n层梯子有多少种爬法。
解法:爬到当前层可以有的爬法=前一层的爬法+前两层的爬法。
非递归的办法是,保存pre,prepre,循环累加。


题目:
求整数的X的平方根,要求返回int。
解法:
二分法。有时会超时,也可以用牛顿法。


题目:
给定一个单词序列,要求单词每行长度固定,不允许单词跨行,词与词之间用空格填充。
解法:
逐一统计长度,超过长度时填充。


题目:
给定一组数字,加1后,返回。
解法:
数字进位。

题目:


Add Binary

题目:

两个二进制相加。

解法:

注意进位。


Merge Two Sorted Lists

题目:
合并两有序单链表。
解法:
双指针

题目:
m*n矩阵,求从左上角到右下角的最短路径长度。
解法:
dp,定义一个数组,用来统计从左上角到该格子的最短路径长度。
matrix[i][j] = min{matrix[i - 1][j],  matrix[i][j - 1]}  + array[i][j]

题目:
在01的格子里面,求出所有从左上角到右下角的路径数目。
解法:
dp,遇到1则表示有阻碍

题目:
在格子里面,求出所有从左上角到右下角的路径数目。
解法:
dp,定义数组,逐步求和。
























Count and Say


Sudoku Solver


Valid Sudoku


Search Insert Position


Search for a Range


Search in Rotated Sorted Array

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值