leetcode题目记录

来源:互联网 发布:cad不能访问到网络锁 编辑:程序博客网 时间:2024/06/06 00:43

leetcode题目

1.TwoSum

a.先排序,再夹逼时间复杂度nlogn 空间复杂度o(n)

b.用hash map,时间和空间复杂度均为O(n)。

2.Add Two Numbers

a.直接顺序相加即可,时间复杂度O(n),用l1 || l2 || carry作为循环的判断条件,carry代表进位

3.Longest Substring Without Repeating Characters

a.使用unordered_map保存字符及它们的位置。O(n)。算法中保存三个遍历,目前合法字符串的起始点,目前合法字符串的长度,以及要求的结果。

4.Median of Two Sorted Arrays

a.经典题目,二分法的经典题目。注意边界条件,k=1,m=0或者A[pa-1]==B[pb-1]。递归式子为return findKth(a + pa, m - pa, b, n, k - pa),以及return findKth(a, m, b + pb, n - pb, k - pb);

5.Longest Palindromic Substring

a.经典二维DP。

6.ZigZag Conversion

a.注意numRows为1时,直接返回原来的string

7.Reverse Integer

a.unsigned long long,注意上溢和下溢。另外,比较不同类型时,需要进行强制转换。低阶转高阶。

8.String to Integer (atoi)

流程:

0. 用一个unsigned long long ret来记录结果,用bool isNeg记录正负。

1. 跳过所有起始空格,找到第一个非空格字符c0
(a) c0非数字字符(包括\0)也非正负号,返回0。
(b) c0为+或-,若下一个字符c1为数字,用isNeg记录下正或负,并移到c1。否则返回0。
(c) c0为数字,则更新ret

2. 经过1已经找到了第一个数字字符,以及结果的正负。此时只需要逐个扫描后面的数字字符,并更新ret即可。终止的条件有以下几个:
(a) 扫到一个非数字字符,包括\0和空格。返回ret
(b) ret已经超出int的范围,返回相应的INT_MIN/INT_MAX
9.Palindrome Number
a.找到最高位和最低位,比较之后,去掉。再比较剩下的数字。
11.Container With Most Water
a.双指针向中间进行搜索,短板向中间走。因为往中间走,代表宽度减小,那么宽度小的时候,只有遇上更高的height才能组成更加大的container。
12.Integer to Roman
a.解法非常妙,使用了两个数组,一个保存string,一个保存数字(1000, 900, 500, 400 100......),然后遍历数字数组,进行转换。
13.Roman to Integer
a.解法非常巧妙,使用了hash_map保存(I,1),(V,5)这种映射关系,然后当前字母对应的数字比前面大,则结果减去2*ht[s[i-1]]
14.Longest Common Prefix
a.解法很巧妙,我的想法总是两次。而实际上一次遍历就足够了。
15.3Sum
a.要过滤重复,需要做两点。第一点是第一个数上做文章,if(i>0 && num[i]==num[i-1]) continue;,
第二点是第二个和第三个数上做文章,
  left++;  right--;  while(num[left]==num[left-1]) left++;  while(num[right]==num[right+1]) right--;

16.3Sum Closest

a.双指针扫描,注意解法中维护的变量是误差,在这里,误差可正可负。

int diff = num[i]+num[left]+num[right]-target;if(abs(diff)<abs(minDiff)) minDiff = diff;

17.Letter Combinations of a Phone Number

a.一开始想的使用回溯法,但是会用到一个hash map,key保存数字,value为对应的字符串。后来看到更方便的定义形式。string dict[] = {" ","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};

b.注意,此题的非递归做法!!!也就是插入法。要充分理解插入法的逻辑。注意在结果中要先插入空字符串,如下:

vector<string> lettComb;lettComb.push_back("");
18.4Sum

a.用传统方法,四个指针方法。i != 0 && nums[i] == nums[i-1], j != i+1 && nums[j] == nums[j-1]

b.用hash_map方法。map的key为两个元素的和,value为两个元素的坐标。通过下列语句去重。if(isFirstPush || (res.back())[2] != num[sum2[k].first])。

19.Remove Nth Node From End of List

a.快慢指针,快指针先走n步,慢指针再走。

20.Valid Parentheses

a.经典的进栈和出栈问题

21.Merge Two Sorted Lists

a.需要注意的一点是,当一个list为空时,结果链表指针link可以直接->next=list

22.Generate Parentheses

a.只要(的数目小于n,就可以插入(。而可以插入)的前提是当前(的数目多余)的数目

23.Merge k Sorted Lists

a.用优先级队列(最小堆),时间复杂度为O(nklogk),空间复杂度为O(k),priority_queue,要会写cmp函数,以及如何声明priority_queue

b.循环两两合并。时间复杂度为O(n*k^2)。

c.二分合并,时间复杂度为O(nklogk),空间复杂度为O(1),二分非递归合并用到了两个while循环,和begin和end循环变量,非常精巧。

24.Swap Nodes in Pairs

a.使用三个指针,扫描完成。

25.Reverse Nodes in k-Group

a.题目要求只用常量空间。所以在这里不能用数组栈。最好的方法是把一段段链表看成goup,翻转group里面的节点

26.Remove Duplicates from Sorted Array

a.双指针,一个扫描数组,一个用来记录不重复数字序列的后面那一位。nums[left++] =nums[i++]。

27.Remove Element

a.同样是双指针解法。nums[left++]=nums[i++];

29.Divide Two Integers

a.计算左移多少次可以使pDivisor<<numShift刚刚大于pDividend,另外需要注意一些边界条件。

  while (lDividend>=lDivisor) {
            int shift_num = 0;
            while (lDividend>=(lDivisor<<shift_num)) {
                shift_num++;
            }
            ret += (1<<(shift_num-1));
            lDividend -=(lDivisor<<(shift_num-1));
  }

30.Substring with Concatenation of All Words

a.滑动窗口解法。把每个单词当成一个字符那样对待。

for(int i = 0; i < wordLen; i++)
        {//为了不遗漏从s的每一个位置开始的子串,第一层循环为单词的长度
            unordered_map<string, int>wordTimes2;//当前窗口中单词出现的次数
            int winStart = i, cnt = 0;//winStart为窗口起始位置,cnt为当前窗口中的单词数目
            for(int winEnd = i; winEnd <= (int)S.size()-wordLen; winEnd+=wordLen)

31.Next Permutation

a.从右至左,找一个a[i]<a[i+1]的数.从右至左找到第一个比a[i]大的元素。交换这两个元素。翻转[i+1, n-1]。记牢。

32.Longest Valid Parentheses

a.DP做法。dp[i]代表以s[i-1]为结尾的longest valid parentheses substring的长度。时间复杂度为O(n)。递推公式为:

s[i-1] = '(':
DP[i] = 0


s[i-1] = ')':找i前一个字符的最长括号串DP[i-1]的前一个字符j = i-2-DP[i-1]
DP[i] = DP[i-1] + 2 + DP[j],如果j >=0,且s[j] = '('
DP[i] = 0,如果j<0,或s[j] = ')'

DP[0] = 0

b.stack做法,stack保存下标。

33.Search in Rotated Sorted Array I

a.原数组0 1 2 3 4 5 6,然后旋转2 3 4 5 6 0 1和5 6 0 1 2 3 4,找规律。要求数字正好是7个。向右旋转2个或者5个。通过比较nums[mid]和nums[end]发现有序的那半边。

34.Search for a Range

a.需要分别查找左边界和右边界。

对搜索left:如果target <= A[mid]则继续向左找,否则向右找。直到搜索结束,left = start
对搜索right:如果target >= A[mid]则继续向右找,否则向左找。直到搜索结束,right = end
if(start>=0 && start<n && A[start]==target) return start;
35.Valid Sudoku
a.依次检查每一行、每一列以及每一个九宫格的数字元素是否在1-9之间,并且是否没有重复。
36.Sudoku Solver
a.depth从0到80,一层层地填写为'.'的空格。需要注意两点,1.用了三个二维数组来维护每行每列每个小9宫格中数字某某是否出现。2.dfs函数的返回值,在这里返回bool,用返回类型来区分当前空格填写某个数字,是否可以走得通。
38. Count and Say
a.简单地模拟就行了。
39.Combination Sum

a.关键在于去重。需要注意三点:1.后面选择数的index一定>=前面选择的数的index。2.不考虑跟上一个数字重复的数字。

例1:

for(int i = index; i < candidates.size() && target >= candidates[i]; i++)

例2:

if(i == 0 || candidates[i] != candidates[i-1])

40.Combination Sum II

a.在for循环中,也就是某layer尝试放入下一个数y的时候,我们提前判断下一个数y是否跟当前的数x相等,如果相等,我们就直接跳过去,因为第一次放x的时候,实际上已经包含了conbination包含x的所有可能性,再尝试y就会产生重复。

for(int i=start; i<num.size(); i++) {            if(i>start && num[i]==num[i-1]) continue;

41.First Missing Positive

a.如果一道题用O(n)时间,常量空间,那么要向桶排序和计数排序进行思考,这道题用输入数组用来做桶排序的空间。注意条件A[i]!=A[A[i]-1]是为了避免死循环,比如[1,1]。

 while(i<n) {            if(A[i]!=i+1 && A[i]>0 && A[i]<=n && A[i]!=A[A[i]-1])                swap(A[i],A[A[i]-1]);            else                i++; }
42.Trapping Rain Water
a.对任意位置i,在i上的积水,由左右两边最高的bar:A[left] = max{A[j], j<i}, A[right] = max{A[j], j>i}决定。定义Hmin = min(A[left], A[right]),则积水量Si为:

Hmin <= A[i]时,Si = 0
Hmin > A[i]时,Si = Hmin - A[i]

43.Multiply Strings

a.把两个single digit相乘得到的结果可以保存到一个数组中。注意m位*n位,结果最大为m+n位。

44.Wildcard Matching

a.贪心和回溯,双指针i和j分别在s和p上游动。保存*的位置,以及遇到*时,s的位置。

45.Jump Game II

a.d[k] = max(i+A[i])     d[k-2] < i <= d[k-1].贪心算法。

class Solution {public:    int jump(int A[], int n) {        int curMax = 0, njumps = 0, i = 0;        while(curMax<n-1) {            int lastMax = curMax;            for(; i<=lastMax; i++)                 curMax = max(curMax,i+A[i]);            njumps++;            if(lastMax == curMax) return -1;        }        return njumps;    }};
46.Permutations

a.插入法即可。但是要注意一些细节。比如:

allPer.push_back(vector<int>(1,num[0]));
allPer[j].push_back(num[i]);

47.Permutations II

a.情况与combination II差不多。

if(used[i]) continue;if(i>0 && num[i]==num[i-1] && !used[i-1]) continue;

48.Rotate Image

a.从外到内,对每层进行翻转。

49.Group Anagrams

关键点:如果两个字符串互为anagram,则它们的排序结果一定相同。

50.Pow(x, n)

a.分治递归。

54.Spiral Matrix

a.本题为打印螺旋矩阵。我们逐个环进行打印,对于m*n的矩阵,环的个数为(min(m, n)+1)/2。对于每个环,顺时针打印四条边。需要注意的一点是,最后一个环可能只含有一行或者一列数据。

利用环的思想,并利用环当前的高度和宽度

56.Merge Intervals

a.首先按照start的大小来给所有interval排序,start小的在前。然后扫描逐个插入结果。如果发现当前interval a和结果中最后一个插入的interval b不重合,则插入a到b的后面;反之如果重合,则将a合并到b中。注意要给object排序需要定义一个compare structure作为sort函数的额外参数。

太精彩的算法!!!

57.Insert Interval

a.遍历数组,如果当前interval在newInterval前面,则将当前interval插入结果中;如果当前interval在newInterval后面,则将newInterval插入结果并把newInterval赋值为当前interval;如果两者重叠,则合并两个区间,修改newInterval的数值,这三种情形如下图所示。图中的Current对应newInterval,New对应intervals[i]。

59.Spiral Matrix II

a.我们也是逐个环地进行填充,每个环顺时针逐条边填充。每个环的左上角起点是matrix[i][i],之后顺时针依次填充环的四条边 。

62.Unique Paths

a.数学组合公式或者一维动态规划。

64.Minimum Path Sum

a.动态规划。一维。滚动数组。

65.Valid Number

有限状态自动机,厘清所有状态,以及状态转换。

69.Sqrt(x)

a.牛顿迭代法。

72.Edit Distance

a.dp[i][j] = min(dp[i-1][j-1], min(dp[i-1][j], dp[i][j-1])) + 1;  

73.Set Matrix Zeroe

a.用矩阵的第一行和第一列来记录。

75.Sort Colors

a.这道题目的思想很像快速排序,把0往左边放,把2往右边放。用三个指针进行操作,分别为i,left, right。代码如下:

int left=0, right=n-1;int i = 0;while(i<=right) {if(A[i]==0) 
swap(A[i++],A[left++]);        else if(A[i]==1)                 i++;        else if(A[i]==2)                 swap(A[i],A[right--]);}


76.Minimum Window Substring

a.1.先搜索到满足条件的窗口,然后再试着是否能够从左向右缩小窗口(当前字符不在t中,或者去掉当前字符,剩下的字符仍然是窗口)。2.start = start+1(此时窗口肯定不会包含所有的T中的字符),跳转到步骤1继续寻找下一个窗口.3.用哈希表来映射T中字符以便在O(1)时间内判断一个字符是否在T中,由于是字符缘故,可以用数组简单地来实现;还需要一个哈希表来记录扫描时T中的某个字符在S中出现的次数,也可以用数组实现。

81.Search in Rotated Sorted Array II

a.A[mid] = A[end] != target时:搜寻A[start : end-1]

82.Remove Duplicates from Sorted List II

a.II比I难在需要删除所有的重复节点。这里需要注意两点:1. 头节点也可能被删除。可以使用dummy节点来简化。2. 如果采用与I类似的思路来删除当前节点后所有的重复节点,则完成后还需要把当前节点也删除。因此需要有一个变量来记录当前节点是否有重复

83.Remove Duplicates from Sorted List I

a.由于list已经排序过,重复节点必相邻。由于链表指针是单向的,所以访问每个节点时,判断后一个节点是否是重复节点,如果是,则删除后一个节点。由于I中重复节点要保留一个,如果碰到重复删除后节点,则不需要考虑删除头指针的特殊情况。

84.Largest Rectangle in Histogram

a.仔细考察Brute Force算法,发现问题在于指针重复扫描。以递增序列为例:

0 1 2 3 4 5 6

在计算s[0]的时候扫描了右边6个数,计算s[1]时继续扫描右边5个数,依次类推。而没能利用到这个序列的递增性质。当序列从i递增到j时,bar i~j的面积一定都能扩展到j。而一旦在j+1递减了,那么表示bar i~j中的部分bar k无法继续扩展,条件是h[k]>h[j+1]。

1. 利用这个性质,可以将递增序列cache在一个stack中,一旦遇到递减,则根据h[j+1]来依次从stack里pop出那些无法继续扩展的bar k,并计算面积。

2. 由于面积的计算需要同时知道高度和宽度,所以在stack中存储的是序列的坐标而不是高度。因为知道坐标后很容易就能知道高度,而反之则不行。

85.Maximal Rectangle

a.回想leetcode的另一题计算直方图最大面积:Largest Rectangle in Histogram,它可以在O(n)时间内解决,这一题可以转化成求直方图最大面积问题,对每一行中的每个位置,计算该位置所在列向上的1的连续个数,这样每一行中每个位置1的个数就形成了一个直方图,对每一行调用计算直方图面积的算法,就可以求出最大的面积。下面代码中height就是保存每一行每个位置1的个数,并且和上面解法中求dp类似,每一行的height可以由上一行的height求得。

87.Scramble String

a.动态规划用数组来保存子问题,设dp[k][i][j]表示s2从j开始长度为k的子串是否可以由s1从i开始长度为k的子串转换而成,那么动态规划方程如下

  1.    初始条件:dp[1][i][j] = (s1[i] == s2[j] ? true : false)
  2.    dp[k][i][j] = ( dp[divlen][i][j] && dp[k-divlen][i+divlen][j+divlen] )  ||  ( dp[divlen][i][j+k-divlen] && dp[k-divlen][i+divlen][j] ) (divlen = 1,2,3...k-1, 它表示子串分割点到子串起始端的距离) ,只要一个子问题返回真,就可以停止计算

88.Merge Sorted Array

a.The key to solve this problem is moving element of A and B backwards. If B has some elements leftafter A is done, also need to handle that case.。

89.Gray Code

a.二进制转grey码的公式。gray = (binary) xor (binary >> 1).

95.Unique Binary Search Trees II

a.1. 根节点可以任取min ~ max (例如min  = 1, max = n),假如取定为i。

2. 则left subtree由min ~ i-1组成,假设可以有L种可能。right subtree由i+1 ~ max组成,假设有R种可能。生成所有可能的left/right subtree
3 对于每个生成的left subtree/right subtree组合<T_left(p), T_right(q)>,p = 1...L,q = 1...R,添加上根节点i而组成一颗新树
97.Interleaving String
a.动态规划解法:根据递归的思想,我们如下使用动态规划解此题。设dp[i][j]表示s1[0...i-1]和s2[0...j-1]能否组合成s3[0....i+j-1],动态规划方程如下                                                                              
  • dp[i][j] = (dp[i][j-1] ==true && s3[i+j-1] == s2[j-1]) || (dp[i-1][j] == true && s3[i-1+j] == s1[i-1])
  • 初始条件:if(s1[0] == s3[0])dp[1][0] = true  ,  if(s2[0] == s3[0])dp[0][1] = true; 其他值均为false
98.Validate Binary Search Tree
a.中序遍历,应该是严格递增的。使用pre指针。
99.Recover Binary Search Tree
a.要找到错误交换的两个节点,可以中序遍历,first节点为第一个比前面节点小的节点的前一个节点,sec节点为最后一个比前面小的节点
100.Same Tree
a.
class Solution {public:    bool isSameTree(TreeNode *p, TreeNode *q) {        if(!p && !q) return true;        if((!p && q) || (p && !q)) return false;        if(p->val != q->val) return false;        return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);    }};

101.Symmetric Tree

a.迭代解法本质上和递归解法是一回事,尽管理解上没有递归这么直观简洁。通常用stack或queue来暂时存放之后要处理的节点。这里由于要判断左右子树是否镜像,用两个queue分别对左右子树进行BFS的一层一层地访问。在访问中压入子节点到两个queue时,采用完全相反的顺序:对左子树先压入left child再right child;对右子树先压入right child再left child。这样一来将symmetric tree问题又转换为了same tree问题

105.Construct Binary Tree from Preorder and Inorder Traversal

 a.root->left = helper(pBegin+1, pBegin+1+(rootIter-iBegin), iBegin, rootIter);
    root->right = helper(pBegin+1+(rootIter-iBegin), pEnd, rootIter+1, iEnd);

108.Convert Sorted Array to Binary Search Tree

a.找到数组的中间数据作为根节点,小于中间数据的数组来构造作为左子树,大于中间数据的数组来构造右子树,递归解法如下。

109.Convert Sorted List to Binary Search Tree

    TreeNode* helper(ListNode* start, ListNode* end) {
        if (start==end) {
            return NULL;
        }
        ListNode* fast = start, *slow = start;
        while (fast->next!=end&&fast->next->next!=end) {
            slow = slow->next;
            fast = fast->next->next;
        }
        TreeNode* parent = new TreeNode(slow->val);
        parent->left = helper(start, slow);
        parent->right = helper(slow->next, end);

        return parent;
    }

110.Balanced Binary Tree

a.用height是否为-1标记树是否是平衡的。

class Solution {public:    bool isBalanced(TreeNode *root) {        return findDepth(root)==-1 ? false : true;    }        int findDepth(TreeNode *root) {        if(!root) return 0;                int leftDepth = findDepth(root->left);      // left subtree depth        if(leftDepth==-1) return -1;                int rightDepth = findDepth(root->right);    // right subtree depth        if(rightDepth==-1) return -1;                if(abs(leftDepth-rightDepth)>1) return -1;                return max(leftDepth,rightDepth)+1;    }};

115.Distinct Subsequences

a.动态规划,设dp[i][j]是从字符串S[0...i]中删除几个字符得到字符串T[0...j]的不同的删除方法种类,有上面递归的分析可知,动态规划方程如下

  • 如果S[i] = T[j], dp[i][j] = dp[i-1][j-1]+dp[i-1][j]
  • 如果S[i] 不等于 T[j], dp[i][j] = dp[i-1][j]
  • 初始条件:当T为空字符串时,从任意的S删除几个字符得到T的方法为1。

116和117题

递推:在第i层的所有next pointer都连接好的情况下,如何连接第i+1层的next pointer?
显然从第i层的最左节点开始依次通过next pointer遍历这一层,同时将他们的children,即第i+1层的节点依次通过next pointer连接起来。连接的时候要分情况处理。

初始情况:对于顶层,只有一个节点root,所以该层连接已经完成。


保存first指针和last指针,分别为下一层第一个与下一层最后遍历的元素,还要注意指针是怎样移动的。

这道题的关键之处,就在于想清楚最后两层。!!!

121

维持一个当前最小值,这样的trick需要牢记。

122

资本家心态,不放过一丝一毫可以挣钱的机会!!!!

123

1. 计算A[0:i]的收益最大值:用minPrice记录i左边的最低价格,用maxLeftProfit记录左侧最大收益
2. 计算A[i:n-1]的收益最大值:用maxPrices记录i右边的最高价格,用maxRightProfit记录右侧最大收益。
3. 最后这两个收益之和便是以i为分割的最大收益。将序列从左向右扫一遍可以获取1,从右向左扫一遍可以获取2。相加后取最大值即为答案。

124

ret作为引用参与递归,递归函数的返回值为root或者root加左子树或者root+右子树


126题

注意点1:在求最短搜索路径,树最小深度的问题,使用bfs,实际上树的层序遍历便是bfs。

注意点2:找到一个相邻单词时候,加入bfs队列后,要从字典中删除。不删除会造成死循环。

127题

如何保证求得所有最短路径?

1.当前层遍历完后,再把所有相邻词从dict删除。

2.用hashtable控制不把重复元素放进队列。

如何构造路径?

使用一个hash表,key为单词,value为前驱单词列表。最后递归反向得到路径。

130

bfs中的注意事项!!!bfs!!!!

133.

bfs队列,dfs用stack!!!

只考虑第一层的所有情形,想得过深会让脑细胞不够用!!

134 

挺trick的一道题目,需要反证法思想。

135经典双向扫描。

137

  • 显现a的左起第i+1位: a & 1 << i
  • 把a的左起第i+1位置零:a &= ~(1 << i)
  • 把a的左起第i+1位置1:result |= 1 << i

139

只是求能否break,并且起点和终点都是固定的,所以从二维DP换到一维DP。


140

得到二维数组用于存放前驱路径

141

双指针追赶法

142

推理过程一定要熟记于心。!!!

143

1.找到后半段(起点为L/2+1个节点,L为链表节点数),

2.翻转后半段(翻转后,把后半段第一个节点的next设置为NULL)

3.后半段插入前半段(用right->next是否为NULL控制是否停止)

重要的一个trick是:在翻转后半段的时候,把第L/2+1个节点的next设置为NULL。

145

后续遍历输出的情况只有一种,p的前驱prev的right为p时,这个时候,倒序输出p->left到prev。注意先print,之后再把prev->right=NULL。

146.

用unordered_map和list

147

喜刷刷的解法更简便。

149

  1. 当对每个点,看有多少其他点跟它在一条直线上时候,只看在它后面的点,这样是为了避免重复计算。
  2. 用hash map来保存在某个斜率下,有多少点跟当前点在同一条直线上。在这里斜率是关键!!!!!!

151.注意保存large之前的数值。要谨慎,仔细!!

155.用两个stack,其中一个stack保存目前为止的最小值。

160.如果两个链表相交,则它们的尾巴节点必然相同

162.1.注意找到任何一个peak数就行

2.任意两个相邻的数均不相等。

3.通过nums[mid]和nums[mid+1]进行比较来缩减一半的范围。


164

1.len=(large-small)/n+1

buck_num = (large-small)/len+1

哪个桶(num-small)/len

很有意思的一道题目!精彩!!

0 0
原创粉丝点击