LeetCode高频148错题记录

3. Max Points on a Line 共线点个数3种解法

思路一:思考如何确定一条直线,两点法,确定斜率后带入一点。有三种情况,1. 两点重合,2. 斜率不存在,3. 正常算,依次以每个点为过直线的点,map映射斜率个数。

思路二:后两种情况合并,用(dy/d, dx/d)表示,其中d=gcd(dx, dy),这样避免了除法的精度损失

思路三:暴力O(n^3)的解法,枚举任意一条直线,判断三点共线三角形面积法(1/2*ABxAC==0)叉积为零(行列式为0)

三点共线 

4. O(nlogn)链表排序

思路一:归并排序(注意要做截断处理前一半的末尾赋值为NULL,奇数偶数都考虑可以避免找中间结点的错误,if(two&&two->next))

思路二:快速排序 

 1 class Solution {
 2 public:
 3     ListNode* sortList(ListNode* head) {
 4         quicksort(head,NULL);
 5         return head;
 6     }
 7     void quicksort(ListNode* head, ListNode* end)
 8     {
 9         if(head!=end)
10         {
11             ListNode* partion = partition(head,end);
12             quicksort(head, partion);
13             quicksort(partion->next,end);
14         }
15     }
16     ListNode* partition(ListNode* head, ListNode* end)
17     {
18         ListNode* slow = head;
19         ListNode* fast = head->next;
20         while(fast!=end)
21         {
22             if(fast->val<head->val)
23             {
24                 slow = slow->next;
25                 swap(fast->val,slow->val);
26             }
27             fast = fast->next;
28         }
29         swap(slow->val,head->val);
30         return slow;
31     }
32 };

6 后序遍历非递归

思路一:标记变量法,判断是否为遍历完右子树返回。

要点:标记变量mark,初始化为NULL。后序从右子树返回则mark = root,从左子树返回的,还要重新入栈,注意两个循环的条件都是栈不为空。

思路二: 判断法

入栈顺序根节点,右儿子,左儿子。这样出栈顺序就是后序的顺序,判断条件一定要注意pre!=NULL一定不能忘记写,如果忘记了,pre==NULL时有一个子树为空也满足条件就错了。

if((cur->left==NULL&&cur->right==NULL)||(pre!=NULL&&(cur->left==pre||cur->right==pre)))

一种情况如果左右子树为空,那么直接记录答案并更新pre;另一种情况,如果当前pre不为空并且表示从任意一个子树返回,那么也是正确的后序返回顺序。为什么从左子树返回也是正确的,因为按照左右中的顺序出栈,那么到当前点,pre==cur->left,表明右子树为空。

思路三:左右子树入栈顺序改变的先序遍历+reverse

栈来操作,先:中左右->后:左右中,先反转:右左中,将左右子树入栈顺序调换即可

8. reorder-list. 

 LL0→L1→…→Ln-1→Ln,  变成这样:L0→Ln →L1→Ln-1→L2→Ln-2→… 思路,快慢指针找中点,找到后尾串反转(双指针法,或头插[第二个放前面,第三个放前面..]),两个链表合并。

11. word-break-ii:

枚举所有情况的题,多半是dfs,但如果不用记忆数组做减少重复计算的优化,那么递归方法跟brute force没有区别,所以要避免重复计算。或用dp搞。

 1     vector<string> wordBreak(string s, vector<string>& wordDict) {
 2         unordered_map<string, vector<string>> m;
 3         return helper(s,wordDict,m);
 4     }
 5     vector<string> helper(string s,  vector<string> & dict, unordered_map<string, vector<string>>& m)
 6     {
 7         if(m.count(s))return m[s]; //记忆化搜索
 8         if(s.empty())return {""};  //递归出口
 9         vector<string> res;
10         for(string word:dict)
11         {
12             if(s.substr(0,word.size())!=word)continue;
13             vector<string> remember = helper(s.substr(word.size()),dict,m);
14             for(string str:remember)
15             {
16                 res.push_back(word + (str.empty()?"":" ")+str);
17             }
18         }
19         return m[s]=res;
20     }

single-number-ii

思路:转自菜鸟葫芦娃

Single Number的本质,就是用一个数记录每个bit出现的次数,如果一个bit出现两次就归0,这种运算采用二进制底下的位操作^是很自然的。Single Number II中,如果能定义三进制底下的某种位操作,也可以达到相同的效果,Single Number II中想要记录每个bit出现的次数,一个数搞不定就加两个数,用ones来记录只出现过一次的bits,用twos来记录只出现过两次的bits,ones&twos实际上就记录了出现过三次的bits,这时候我们来模拟进行出现3次就抵消为0的操作,抹去ones和twos中都为1的bits

 1 int singleNumber(int A[], int n) {
 2         int ones = 0; // 记录只出现一次的bits
 3         int twos = 0; // 记录只出现二次的bits
 4         int threes;
 5         for(int i=0;i<n;i++)
 6         {
 7             int t=A[i];
 8             twos |= ones&t; // 要在更新ones之前更新twos
 9             ones ^=t;
10             threes = ones&twos;// ones和twos中都为1即出现3次
11             ones &= ~threes;//抹去出现3次的bits
12             twos &= ~threes;
13         }
14         return ones;
15     }

 Integer break

思路一:O(n^2)的dp,注意拆分的时候,可以不拆分包含自己

 1      dp[2]=1;
 2         dp[3]=2;
 3         for(int i=4;i<=n;i++)
 4         {
 5             for(int j=1;j<i;j++)
 6             {
 7                 dp[i] = max(max(dp[j],j)*max(dp[i-j],i-j),dp[i]);// 注意这里
 8             }
 9         }
10         return dp[n];

思路二:O(n)的dp,和台阶题类似,在n >3的情况下,处理一个数要么拆分成2,要么拆分成3,(4的话相当于2个2 , 拆成1的话乘积太小了)

1         dp[2] = 2
2         dp[3] = 3
3         for(int i=4;i<=n;i++)
4             dp[i] = max(dp[i - 2] * 2, dp[i - 3] * 3)
5         return dp[n]    

思路三:

拆成3的比拆成2的乘积大。 比如6的时候 2*2*2 < 3*3

希望能尽可能的拆成3,然后才是2.

所以,如果

  • n % 3 == 0:  那么全部拆成3
  • n % 3 == 1:  2个2剩下的为3    4*3^(x-1) > 1*3^x
  • n % 3 == 2:  1个2剩下的为3

16 candy

要求每个小朋友至少分一个,rating高的比邻居分的多

思路:分两步,从左到右,从右到左。从右到左时注意,如果此时dp[i]>dp[i+1]则continue,没必要加了,别无脑加1!

18 clone-graph

类比任意指针的链表深拷贝

思路:dfs或bfs遍历复制,用map记录以及visited处理

 1 /**
 2  * Definition for undirected graph.
 3  * struct UndirectedGraphNode {
 4  *     int label;
 5  *     vector<UndirectedGraphNode *> neighbors;
 6  *     UndirectedGraphNode(int x) : label(x) {};
 7  * };
 8  */
 9 class Solution {
10 public:
11     UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
12         unordered_map<int, UndirectedGraphNode *> umap;
13         return clone(node,umap);
14     }
15     UndirectedGraphNode * clone(UndirectedGraphNode* node,unordered_map<int, UndirectedGraphNode *> & umap)
16     {
17         if(!node)return node;
18         if(umap.count(node->label))return umap[node->label];
19         UndirectedGraphNode* newNode = new UndirectedGraphNode(node->label);
20         umap[node->label]=newNode;
21         for(int i=0;i<node->neighbors.size();i++)
22         {
23             newNode->neighbors.push_back(clone(node->neighbors[i],umap));
24         }
25         return newNode;
26     }
27 };

25 word ladder

一次只能变一个字母,且中间状态必须在dict中存在,可能有很多中间状态,求最小转换次数,最小的搜索方式就是bfs

思路:从第一个字母开始改,26个字母分别试,用一个map记录是否访问过以及到这个状态所需的次数。

 1 class Solution {
 2 public:
 3     int ladderLength(string start, string end, unordered_set<string> &dict) {
 4         unordered_map<string,int> pathCnt{{{start,1}}};
 5         queue<string> q{{start}};
 6         while(q.size())
 7         {
 8             string word = q.front();q.pop();
 9             for(int i=0;i<word.size();i++)
10             {
11                 string newWord = word;
12                 for(char ch = 'a';ch<='z';ch++)
13                 {
14                     newWord[i] = ch;
15                     if(newWord==end)return pathCnt[word]+1;
16                     if(dict.count(newWord)&&!pathCnt.count(newWord))
17                     {
18                         q.push(newWord);
19                         pathCnt[newWord]=pathCnt[word]+1;
20                     }
21                 }
22             }
23         }
24         return 0;
25     }
26 }; 

*27 binary-tree-maximum-path-sum

这题之前做过,现在又不会了orz。类似一维数组最大和问题,换到了二叉树上,起点终点在任意结点,可能有负数。

考察递归和树的dfs很好的一道题。

    4
   / \
  11 13
 / \
7   2

试一个例子自己找一下递归的解法

对于每个结点来说,我们要知道经过其左子结点的path之和大还是经过右子节点的path之和大。那么递归函数返回值就可以定义为以当前结点为根结点,到叶节点的最大路径之和,然后全局路径最大值放在参数中,用引用res来表示。

1. 用res记录答案

2. 递归函数始终返回以当前结点为终点的最大路径和

每次递归中,左子结点为终点的最大path和加上以右子结点为终点的最大path和,再加上当前结点值就组成了一条完整的路径。

 1 class Solution {
 2 public:
 3     int maxPathSum(TreeNode *root) {
 4         int res = INT_MIN;
 5         helper(root, res);
 6         return res;
 7     }
 8     int helper(TreeNode *root, int & res)
 9     {
10         if(root==NULL)return 0;
11         int left = max(0,helper(root->left,res));
12         int right = max(0,helper(root->right,res));
13         res = max(res,left+right+root->val);
14         return max(left,right)+root->val;
15     }
16 };

 Maximum Subarray 最大子数组

思路一:dp O(n) dp[i] = max(dp[i-1], 0)+a[i]

思路二:  分治,每次把数组一分为二,分别找出左边和右边的最大子数组之和,然后还要从中间开始向左右分别扫描,求出的最大值分别和左右两边得出的最大值比较取最大的

 1 class Solution {
 2 public:
 3     int maxSubArray(int A[], int n) {
 4         if (n==0) return 0;
 5         return helper(A, 0, n - 1);
 6     }
 7     int helper(int num[],int l,int r)
 8     {
 9         if(l>=r)return num[l];
10         int mid = (l+r)/2;
11         int left = helper(num,l,mid-1);
12         int right = helper(num,mid+1,r);
13         int mmax = num[mid], t = mmax;
14         for (int i = mid - 1; i >= l; --i) {
15             t += num[i];
16             mmax = max(mmax, t);
17         }
18         t = mmax;
19         for (int i = mid + 1; i <= r; ++i) {
20             t += num[i];
21             mmax = max(mmax, t);
22         }
23         return max(max(left,right),mmax);
24     }
25 };

 sqrtx

开根号,返回int

二分法:

注意判出条件有两个。

 1 int sqrt(int x) {
 2         if(x < 0)
 3             return -1;
 4         long l = 0, r = x;
 5         while(l<=r)
 6         {
 7             long mid = l+(r-l)/2;
 8             if(mid*mid==x || (mid * mid < x && (mid + 1) * (mid + 1) > x))return mid;
 9             if(mid*mid<x)l=mid+1;
10             else r = mid-1;
11         }
12         return 0;
13     }

牛顿法:

转自Matrix67

随便设一个近似值x,然后不断令x等于x和a/x的平均数,迭代个六七次后x的值就已经相当精确了。

仅仅是不断用(x,f(x))的切线来逼近方程x^2-a=0的根。根号a实际上就是x^2-a=0的一个正实根,这个函数的导数是2x。也就是说,函数上任一点(x,f(x))处的切线斜率是2x。那么,x-f(x)/(2x)就是一个比x更接近的近似值。代入f(x)=x^2-a得到x-(x^2-a)/(2x),也就是(x+a/x)/2。

1 int sqrt(int x) {
2         if(x < 0)
3             return -1;
4         long res = x;
5         while(res * res > x)
6             res = ((res + x / res) >> 1);
7         return res;
8     }

 Permutation Sequence

求第k个全排列,直接dfs求超时

有个O(n)的好方法,从左到右一个一个求出来。因为只要求1个,所以可以按照全排列的规则,一个个数的求出每个位置的数字,而不需要将所有的全排列字符串列出来。

对于n个字符组成的字符串{1,2,3,...,n},取第k个排列时,首先可以求出第k个排列字符串中的第一个数,即(k-1)/(n-1个数的排列个数)

下面是递归和非递归的两种写法

 1 class Solution {
 2 public:
 3     string res="";
 4     string nums = "123456789";
 5     string getPermutation(int n, int k) {
 6         string s;
 7         for(int i=0;i<n;i++)
 8                 s += nums[i];
 9         solve(s,k);
10         return res;
11     }
12     void solve(string& s, int k)
13     {
14         if(s==""||k==0)return;
15         int len = s.size();
16         int cnt=1; // 计算 (n-1)个数的排列个数cnt
17         for(int i=1;i<len;i++)
18         {
19             cnt*=len-i;
20         }
21         int pos = (k-1)/cnt;
22         res+=s[pos];
23         k-=cnt*pos;
24         s=s.erase(pos,1);
25         solve(s,k);
26     }
27 };
 1 string getPermutation(int n, int k) {
 2         string s;
 3         string res="";
 4         string nums = "123456789";
 5         for(int i=0;i<n;i++)s += nums[i];
 6         for(int i=0;i<n;i++)
 7         {
 8             int cnt=1;
 9             int len=s.size()-1;
10             while(len>1)
11             {
12                 cnt*=len;
13                 len--;
14             }
15             int pos = (k-1)/cnt;
16             k-=cnt*pos;
17             res+=s[pos];
18             s.erase(pos,1);
19         }
20         return res;
21     }

 pascals-triangle-ii

只用O(k)空间返回第k个杨辉三角数组

类比01背包,容量倒循环写法省空间

 1 class Solution {//合理规划dp转移方向降低一个维度
 2 public:
 3     vector<int> getRow(int rowIndex) {
 4         vector<int> dp(rowIndex+1,0);
 5         dp[0]=1;
 6         for(int i=1;i<=rowIndex;i++)
 7             for(int j=i;j>0;j--)
 8             {
 9                 dp[j]=dp[j]+dp[j-1];
10             }
11         return dp;
12     }
13 };

 populating-next-right-pointers-in-each-node 

将完全二叉树改写成next指针指向右边结点的形式,这题之前做过,不用多余空间又忘了怎么搞了orz。应该考虑连续两层的信息。只考虑当前层肯定就断了,不要一直卡在这里。

 1 void connect(TreeLinkNode *root) {
 2         if(!root)return;
 3         while(root->left)
 4         {
 5             TreeLinkNode* p=root;
 6             while(p)
 7             {
 8                 p->left->next = p->right;
 9                 if(p->next)
10                     p->right->next = p->next->left;
11                 else 
12                     p->right->next = NULL;
13                 p=p->next;
14             }
15             root = root->left;
16         }
17     }

populating-next-right-pointers-in-each-node II

凉凉,上一题变一下形又凉了,这题树变为任意二叉树,要灵活运用dummy结点处理,这样从起始到结束都是统一的形式,方便循环处理。当找第一个结点的情况与找后序存在的结点情况相似时,考虑dummy伪结点的使用

 1 void connect(TreeLinkNode *root) {
 2         if(!root)return;
 3         while(root)
 4         {
 5             TreeLinkNode* dummy = new TreeLinkNode(-1); // 每一层的头结点
 6             TreeLinkNode* cur = dummy;
 7             while(root)
 8             {
 9                 if(root->left)
10                 {
11                     cur->next = root->left;
12                     cur = cur->next;
13                 }
14                 if(root->right)
15                 {
16                     cur->next = root->right;
17                     cur = cur->next;
18                 }
19                 root=root->next;
20             }
21             root = dummy->next;
22         }
23     }

distinct-subsequences

dp :当前位置选或不选

array[i][j] 表示T[0, j] 在S[0, i] 中的匹配个数
如果不使用S[i] , 那么f(i , j) = f(i-1 , j)
如果使用了S[i] , 那么一定有 S[i] == T[j] , f( i , j ) = f(i -1 , j -1 )
所以当S[i]==T[j]时,有array( i , j ) = array( i -1 , j ) + array(i - 1 , j - 1);
当S[i]!=T[j]时,有 array( i , j ) = array( i -1 , j );
在使用中不难发现该dp二维数组可以降维,注意改变数组元素值时由后往前,这样不会覆盖[i-1]的值,dummy处理很巧
 1 int numDistinct(string S, string T) {
 2         if(S.size()==0||T.size()==0)return 0;
 3         vector<int> dp(T.size()+1,0);
 4         dp[0]=1;//dummy 
 5         int len = T.size();
 6         for(int i=1;i<=S.size();i++)
 7         {
 8             for(int j=min(i,len);j>0;j--)
 9                 if(S[i-1]==T[j-1])
10                     dp[j]=dp[j-1]+dp[j];
11         }
12         return dp[len];
13     }

 binary-tree-level-order-traversal-ii 

倒序的层序遍历

1 bfs层序遍历后reverse,虽然能做但这个方法太low不符合命题者出题意图

2 查出max depth后,dfs从第一层max_depth到最后一层1,用一个数组存储

3 递归版bfs,很巧,不直接存到result中,先进行下一层递归,存最后一层然后一层一层递归上来,再把当前层的结果保存到result中 。

binary-tree-zigzag-level-order-traversal

1. 这题用bfs+size+tag-reverse可以搞,但比较low

2. 可以双栈搞。注意第二个栈的入栈顺序,先右再左

rotate-image

先关于主对角线对称,然后关于中间行对称。

* merge-k-sorted-lists

1. merge sort 分治做

2. 优先队列最小堆做

分布式常考,解法见blog

分治写法

 1 /**
 2  * Definition for singly-linked list.
 3  * struct ListNode {
 4  *     int val;
 5  *     ListNode *next;
 6  *     ListNode(int x) : val(x), next(NULL) {}
 7  * };
 8  */
 9 class Solution {
10 public:
11     ListNode *mergeKLists(vector<ListNode *> &lists) {
12         if (lists.size() == 0) return NULL;
13         return merge(lists,0,lists.size()-1);
14     }
15     ListNode* merge(vector<ListNode *> &lists, int l,int r)
16     {
17         if(l<r) {
18             int mid = (l+r) / 2;
19             ListNode* left = merge(lists, l,mid);
20             ListNode* right = merge(lists, mid+1,r);
21             return mergeTwoLists(left,right);
22         }
23         else return lists[l];
24     }
25     ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) {
26         ListNode *head = new ListNode(-1);
27         ListNode *cur = head;
28         while (l1 && l2) {
29             if (l1->val < l2->val) {
30                 cur->next = l1;
31                 l1 = l1->next;
32             } else {
33                 cur->next = l2;
34                 l2 = l2->next;
35             }
36             cur = cur->next;
37         }
38         if (l1) cur->next = l1;
39         if (l2) cur->next = l2;
40         return head->next;
41     }
42 };

最小堆写法:

首先把k个链表的首元素都加入最小堆中,它们会自动排好序。然后我们每次取出最小的那个元素加入我们最终结果的链表中,然后把取出元素的下一个元素再加入堆中,下次仍从堆中取出最小的元素做相同的操作,以此类推,直到堆中没有元素了,此时k个链表也合并为了一个链表,返回首节点

 1 struct cmp {
 2     bool operator () (ListNode *a, ListNode *b) {
 3         return a->val > b->val;
 4     }
 5 };
 6  
 7 class Solution {  
 8 public:  
 9     ListNode *mergeKLists(vector<ListNode *> &lists) {  
10         priority_queue<ListNode*, vector<ListNode*>, cmp> q;//最小堆
11         for (int i = 0; i < lists.size(); ++i) {
12             if (lists[i]) q.push(lists[i]);
13         }
14         ListNode *head = NULL, *pre = NULL, *tmp = NULL;
15         while (!q.empty()) {
16             tmp = q.top();
17             q.pop();
18             if (!pre) head = tmp;
19             else pre->next = tmp;
20             pre = tmp;
21             if (tmp->next) q.push(tmp->next);
22         }
23         return head;
24     }  
25 }; 

 remove-duplicates-from-sorted-array-ii

给一个排好序的数组,最多允许2个重复元素。注意要和筛选后的数组比较,不是和原数组比较

 1 int removeDuplicates(int A[], int n) {
 2      
 3         if(n<3)return n;
 4         int index=2;
 5         for(int i=2;i<n;i++)
 6         {
 7             if(A[i]!=A[index-2])
 8                 A[index++]=A[i];
 9         }
10         return index;
11     }

 subsets

递增的全排列,要考虑初始数组不是顺序的情况,dp来做看代码,追加法写的很好,最后按元素数排序输出

 1 vector<vector<int> > subsets(vector<int> &S) {
 2         vector<vector<int>> result;
 3         vector<vector<int>> res;
 4         vector<int> v;
 5         result.push_back(v);
 6         res.push_back(v);
 7         for(int i=0;i<S.size();i++){
 8             int n = result.size();
 9             for(int j=0;j<n;j++){
10                 result.push_back(result[j]);
11                 result[j].push_back(S[i]);
12                 sort(result[j].begin(), result[j].end());// 原数组不是有序数组
13             }
14               
15         }
16         sort(result.begin(), result.end());
17         for(int j=1;j<=S.size();j++)
18             for(int k=0;k<result.size();k++)
19                 if(result[k].size() == j)
20                     res.push_back(result[k]);
21         return res;
22     }

 combinations

sb了这题,看清样例是啥再做行吗,又tm上来就无脑dfs,人家是有序无重复的结果

 1 vector<vector<int> > combine(int n, int k)
 2     {
 3         vector<vector<int>> res;
 4         vector<int> tmp;
 5         dfs(n,k,1,res,tmp);
 6         return res;
 7     }
 8     void dfs(int n,int k,int start,vector<vector<int>> &res,vector<int> tmp)
 9     {
10         if(tmp.size() == k)
11         {
12             res.push_back(tmp);
13             return;
14         }
15         for(int i=start;i<=n;i++)
16         {
17             tmp.push_back(i);
18             dfs(n,k,i+1,res,tmp);
19             tmp.pop_back();
20         }
21     }

set-matrix-zeroes

使用常数空间,如果矩阵某个元素为0,则其所在行和列都为0

方法1. 主要防止覆盖问题,可以先用-1处理所在行和列元素,最后还原为0。

方法2. 用第一行和第一列作为统计标识,首先要判断第一行和第一列是否需要清零,然后遍历内部矩阵并记录,再根据记录清零,最后看是否需要清零第一行第一列。

Simplify Path

字符串好题

思路一:针对每一个需求分别处理

 1   string simplifyPath(string path) {
 2         int pos;
 3         while((pos = path.find("//"))!=-1) //去掉//
 4         {
 5             path.erase(pos,1);
 6         }
 7         while((pos = path.find("/./"))!=-1) //去掉/./
 8         {
 9             path.erase(pos,2);
10         }
11         while((pos = path.find("/../"))!=-1)//去掉/../
12         {
13             path.erase(pos,3);
14             if(pos==0)continue;
15             int p = path.rfind("/",pos-1);
16             path.erase(p,pos-p);
17         }
18         if(path.size()>2&&path.substr(path.size()-3,3)=="/.."){//去掉/..
19             path.erase(path.size()-2,2);
20             if(int(path.size())-2>=0){
21             int p = path.rfind("/",path.size()-2);
22             path.erase(p,path.size()-1-p);
23             }
24         }
25         if(path.size()>1&&path.substr(path.size()-2,2)=="/.")path.erase(path.size()-1,1); //去掉/.
26         if(path.size()>1&&path[path.size()-1]=='/')path.erase(path.size()-1,1);//处理结尾
27         return path;
28     }

思路二:遍历一遍,遍历过程中综合考虑各种因素

 1 class Solution {
 2 public:
 3     string simplifyPath(string path) {
 4         vector<string> v;
 5         int i = 0;
 6         while (i < path.size()) {
 7             while (path[i] == '/' && i < path.size()) ++i;
 8             if (i == path.size()) break;
 9             int start = i;
10             while (path[i] != '/' && i < path.size()) ++i;
11             int end = i - 1;
12             string s = path.substr(start, end - start + 1);
13             if (s == "..") {
14                 if (!v.empty()) v.pop_back(); 
15             } else if (s != ".") {
16                 v.push_back(s);
17             }
18         }
19         if (v.empty()) return "/";
20         string res;
21         for (int i = 0; i < v.size(); ++i) {
22             res += '/' + v[i];
23         }
24         return res;
25     }
26 };

 %是带有符号的,这点要注意

spiral-matrix-ii

螺旋数组正确写法(JAVA)

 1 public int[][] generateMatrix(int n) {
 2         int[][] res = new int[n][n];
 3         if (n < 1)
 4             return res;
 5         int index = 1, rowStart = 0, rowEnd = n - 1, colStart = 0, colEnd = n - 1;
 6         while (index <= n * n) {
 7             for (int i = colStart; i <= colEnd; i++) {
 8                 res[rowStart][i] = index++;
 9             }
10             for (int i = rowStart + 1; i <= rowEnd; i++) {
11                 res[i][colEnd] = index++;
12             }
13             for (int i = colEnd - 1; i >= colStart; i--) {
14                 res[rowEnd][i] = index++;
15             }
16             for (int i = rowEnd - 1; i > rowStart; i--) {
17                 res[i][colStart] = index++;
18             }
19  
20             rowStart += 1;
21             rowEnd -= 1;
22             colStart += 1;
23             colEnd -= 1;
24  
25         }
26  
27         return res;
28     }

 spiral-matrix

 1 while(l<=r&&up<=down)
 2         {
 3             for(int i=l;i<=r;i++)
 4             ans.push_back(matrix[up][i]);
 5             for(int i=up+1;i<=down;i++)
 6             ans.push_back(matrix[i][r]);
 7             for(int i=r-1;up!=down&&i>=l;i--) //由于矩阵是任意的,注意往返不能重复
 8             ans.push_back(matrix[down][i]);
 9             for(int i=down-1;l!=r&&i>up;i--)//注意往返不能重复
10             ans.push_back(matrix[i][l]);
11             l++,r--,up++,down--;
12         }

search-for-a-range

两次二分夹逼法,注意等号的选择

 1 int l1=0,r1=n-1,l2=0,r2=n-1;
 2         while(l1 <= r1)
 3         {
 4             int mid = l1+((r1-l1)>>1);
 5             if(A[mid] < target)
 6                 l1 = mid + 1;
 7             else
 8                 r1 = mid - 1;
 9         }
10         while(l2 <= r2)
11         {
12             int mid = l2+((r2-l2)>>1);
13             if(A[mid] > target)
14                 r2 = mid - 1;
15             else
16                 l2 = mid + 1;
17         }
18         vector<int> result(2,-1);
19         if(l1 <= r2)
20         {
21             result[0] = l1;
22             result[1] = r2;
23         }
24         return result;
25     }

 first-missing-positive 

思路:将数值和位置形成映射,使得A[i]=i+1

 1 int firstMissingPositive(int A[], int n) {
 2         for(int i=0;i<n;)
 3         {
 4             if((A[i]<=0||A[i]>n)&&i<n)
 5             {
 6                 i++;
 7             }
 8             else if(A[A[i]-1]!=A[i])// 注意不能直接写A[i]!=i+1  因为[1,1]可能死循环
 9             {
10                 swap(A[i],A[A[i]-1]);
11             }
12             else i++;
13             
14         }
15         int i;
16         for(i=0;i<n;i++)
17             if(A[i]!=i+1)break;
18         return i+1;
19     }

search-in-rotated-sorted-array

旋转排序数组找target

思路:二分后,一定按顺序的找,注意等号

 1 int search(int A[], int n, int target) {
 2         int l=0,r=n-1;
 3         while(l<=r)
 4         {
 5             int mid = (l+r)/2;
 6             if(A[mid]==target)return mid;
 7             if(A[l]<=A[mid])//等号必须在这里,eg:[3,1] 1
 8             {
 9                 if(A[l]<=target&&target<A[mid])
10                     r=mid-1;
11                 else l=mid+1;
12             }
13             else 
14             {
15                 if(A[mid]<target&&target<=A[r])
16                     l=mid+1;
17                 else r=mid-1;
18             }
19         }
20         return -1;
21     }

 unique-binary-search-trees 

分阶段,先看根节点,可以1到n,当根节点确定时找状态转移,因为是bst,所以左子树为i-1个结点,右子树为n-i个结点,两者乘积

 1     int dp[1000]={0};
 2     int numTrees(int n) {
 3         dp[1]=1;
 4         dp[0]=1;
 5         for(int i=2;i<=n;i++)
 6         {
 7             for(int j=1;j<=i;j++)
 8             dp[i]+=dp[j-1]*dp[i-j];
 9         }
10         return dp[n];
11     }

unique-binary-search-trees II

 1   vector<TreeNode *> generateTrees(int n) {
 2           return dfs(1,n);
 3     }
 4     vector<TreeNode *> dfs(int l,int r){
 5           vector<TreeNode*> ans;
 6           if(l>r) {ans.push_back(NULL);return ans;}
 7           for(int i=l;i<=r;i++){
 8               vector<TreeNode*> vecl=dfs(l,i-1),vecr=dfs(i+1,r);
 9               for(auto p:vecl)
10                   for(auto q:vecr){
11                       TreeNode *root=new TreeNode(i);
12                       root->left=p;
13                       root->right=q;
14                       ans.push_back(root);
15                   }
16           }
17           return ans;
18     }

minimum-window-substring 

尺取法O(n), 在S串中遍历一次,找到包含T串中所有元素的最小子串

思路:因为无序所以hash,map记录元素及对应个数,保留T串整体信息

l=0,r从头向后遍历,直到子串中元素满足map的记录,用count==T.size()判断,记录子串,l右移直到越过第一个T中的元素使子串不满足map条件,此时确定l边界,r右移确定右边界

 1 class Solution {
 2 public:
 3     string minWindow(string S, string T) {
 4     string res;
 5         map<char,int> t,s;
 6         for(auto c:T)
 7             t[c]++;
 8         int count=0,l=0;
 9         for(int r=0;r<S.length();r++)
10         {
11             if(t[S[r]]!=0)
12             {
13                 s[S[r]]++;
14                 if(s[S[r]]<=t[S[r]])
15                     count++;
16                 while(count==T.size())
17                 {
18                     if(res.empty()||res.length()>r-l+1)
19                         res=S.substr(l,r-l+1);
20                     if(t[S[l]])
21                     {
22                         s[S[l]]--;
23                         if(s[S[l]]<t[S[l]])
24                             count--;
25                     }
26                     l++;
27                 }
28             }
29         }
30         return res;
31     }
32 };

 longest-common-prefix:对所有string排序后比较首尾即可

 roman-to-integer:当前一个罗马数字小于后一个时是特例,要减去2倍的前一个数字值

regular-expression-matching

方法一:递归求解

先来判断p是否为空,若为空则根据s的为空的情况返回结果。当p的第二个字符为*号时,由于*号前面的字符的个数可以任意,可以为0,那么先用递归来调用为0的情况,就是直接把这两个字符去掉再比较,或者当s不为空,且第一个字符和p的第一个字符相同时,我们再对去掉首字符的s和p调用递归,注意p不能去掉首字符,因为*号前面的字符可以有无限个;如果第二个字符不为*号,那么就的比较第一个字符,然后对后面的字符串递归

 1 class Solution {
 2 public:
 3     bool isMatch(string s, string p) {
 4         if (p.empty()) return s.empty();
 5         if (p.size() > 1 && p[1] == '*') {
 6             return isMatch(s, p.substr(2)) || (!s.empty() && (s[0] == p[0] || p[0] == '.') && isMatch(s.substr(1), p)); //注意这里
 7         } else {
 8             return !s.empty() && (s[0] == p[0] || p[0] == '.') && isMatch(s.substr(1), p.substr(1));
 9         }
10     }
11 };

方法二:dp

我们也可以用DP来解,定义一个二维的DP数组,其中dp[i][j]表示s[0,i)和p[0,j)是否match,然后有下面三种情况:

1.  P[i][j] = P[i - 1][j - 1], if p[j - 1] != '*' && (s[i - 1] == p[j - 1] || p[j - 1] == '.');
2.  P[i][j] = P[i][j - 2], if p[j - 1] == '*' and the pattern repeats for 0 times;
3.  P[i][j] = P[i - 1][j] && (s[i - 1] == p[j - 2] || p[j - 2] == '.'), if p[j - 1] == '*' and the pattern repeats for at least 1 times.

 1 class Solution {
 2 public:
 3     bool isMatch(string s, string p) {
 4         int m = s.size(), n = p.size();
 5         vector<vector<bool>> dp(m + 1, vector<bool>(n + 1, false));
 6         dp[0][0] = true;
 7         for (int i = 0; i <= m; ++i) {
 8             for (int j = 1; j <= n; ++j) {
 9                 if (j > 1 && p[j - 1] == '*') {
10                     dp[i][j] = dp[i][j - 2] || (i > 0 && (s[i - 1] == p[j - 2] || p[j - 2] == '.') && dp[i - 1][j]);
11                 } else {
12                     dp[i][j] = i > 0 && dp[i - 1][j - 1] && (s[i - 1] == p[j - 1] || p[j - 1] == '.');
13                 }
14             }
15         }
16         return dp[m][n];
17     }
18 };

divide-two-integers:倍增法用的好,减少循环次数

题意:不能用乘法,除法,取余操作,实现整数除法

 1 int divide(int dividend, int divisor) {
 2         if(divisor == 0)
 3             return INT_MAX;
 4              
 5         long long result=0,flag=0;
 6         if((dividend<0)^(divisor<0))
 7             flag=1;
 8          
 9         long long a = abs(dividend), b = abs(divisor);
10         while(a >= b)
11         {
12             long long k = 1, t = b;
13             while(a >= t)
14             {
15                 a -= t;
16                 result += k;
17                 k <<= 1;
18                 t += t;
19             }
20         }
21         return flag?-result:result;
22     }

longest-substring-without-repeating-characters

双指针维护区间,尺取法枚举可能解,这里的词典只记录字母是否出现,并不记录位置。

 1 int lengthOfLongestSubstring(string s) {
 2         int len = s.size();
 3         int dic[128]={0};
 4         int ans=0,p=0;
 5         for(int q=0;q<len;q++)
 6         {
 7            while(dic[s[q]])
 8            {
 9                dic[s[p]]--;
10                p++;
11            }
12             ans=max(ans,q-p+1);
13             dic[s[q]]++;
14         }
15         return ans;
16     }

 longest-palindromic-substring

二维dp 0:不回文,1:回文。注意奇数偶数的初始情况。

 1 string longestPalindrome(string s) {
 2     int len = s.length(),ans;
 3     int dp[len+1][len+1];
 4     memset(dp,0,sizeof(dp));
 5     int rec[len+1];
 6     for(int i = 0;i < len;i++) dp[i][i] = 1,ans = 1,rec[1] = 0;
 7     for(int i = 0;i < len-1;i++) if(s[i] == s[i+1]) ans = 2,rec[2] = i,dp[i][i+1] = 1;
 8     for(int i = 3;i <= len;i++){
 9         for(int j = 0;j <= len-i;j++){
10             if(s[j] == s[j+i-1]) {
11                 dp[j][i+j-1] = dp[j+1][i+j-2];
12                 if(dp[j][i+j-1] == 1) {ans = max(ans,i),rec[i] = j;}
13             }
14         }
15     }
16     return s.substr(rec[ans],ans);
17 }

 median-of-two-sorted-arrays

分治,注意递归的出口,一个是k=1,另一个是边界

 1 double findMedianSortedArrays(int A[], int m, int B[], int n) {
 2         if((m+n)%2==0)
 3             return (help(A,0,m,B,0,n,(m+n)/2)+help(A,0,m,B,0,n,(m+n)/2+1))/2;
 4         else 
 5             return help(A,0,m,B,0,n,(m+n)/2+1);
 6     }
 7     double help(int A[], int aStart, int m, int B[], int bStart, int n, int k)
 8     {
 9         if(aStart >= m)
10             return B[bStart+k-1];
11         if(bStart >= n)
12             return A[aStart+k-1];
13         if(k==1)
14             return min(A[aStart],B[bStart]);
15         int aMin = 0x7fffffff, bMin = 0x7fffffff;
16         if(aStart + k / 2 - 1 < m)
17             aMin = A[aStart + k/2 -1];
18         if(bStart + k / 2 - 1 < n)
19             bMin = B[bStart + k / 2 - 1];
20         if(aMin<bMin)
21             return help(A,aStart+k/2,m,B,bStart,n,k-k/2);
22         else
23             return help(A,aStart,m,B,bStart+k/2,n,k-k/2);
24     }

 3sum 

选出数组中相加为0的3个数,要求非递减,不重复

思路:fix一个数转换为2sum+set会超时

先排序,固定一个数,确定后两个数的和。在后面的数组中两头向中间找,注意剪枝与去重的操作

 1 vector<vector<int> > threeSum(vector<int> &num) {
 2         vector<vector<int> > ans;
 3         sort(num.begin(),num.end());
 4         if(num.empty()||num.front()>0||num.back()<0)return {};
 5         for(int i=0;i<num.size()-2;i++)
 6         {
 7             if(num[i]>0)break;
 8             if(i>0&&num[i]==num[i-1])continue;
 9             int target = 0 - num[i];
10             int j=i+1,k=num.size()-1;
11             while(j<k)
12             {
13                 if(num[j]+num[k]==target)
14                 {
15                     ans.push_back({num[i],num[j],num[k]});
16                     while(j<k&&num[j]==num[j+1])j++;
17                     while(j<k&&num[k]==num[k-1])k--;
18                     j++,k--;
19                 }
20                 else if(num[j]+num[k]<target)
21                     j++;
22                 else 
23                     k--;
24             }
25         }
26         return ans;
27     }

 reverse-nodes-in-k-group

这题考察链表反转,头插的双指针写法

 1 ListNode *reverseKGroup(ListNode *head, int k) {
 2     if(head == NULL || head->next == NULL || k < 2) return head;
 3         ListNode* dummy = new ListNode(0);
 4         dummy->next = head;
 5         ListNode* pre = dummy, *cur = head, *temp;
 6         int len = 0;
 7         while (head != NULL) {
 8             len++ ;
 9             head = head->next;
10         }
11         for (int i = 0; i < len / k; i ++ ) {
12             for (int j = 1; j < k; j ++ ) {
13                 temp = cur->next;
14                 cur->next = temp->next;
15                 temp->next = pre->next;
16                 pre->next = temp;
17             }
18             pre = cur;
19             cur = cur->next;
20         }
21         return dummy->next;
22     }

 generate-parentheses

产生括号的全排列

思路:全排列,容易想到dfs,由于括号包含左括号和右括号两部分,所以一方面不用考虑左括号,让他不断生成就好,对于右括号,设置两种情况,分别是生成和不生成右括号。

 1 vector<string> generateParenthesis(int n) {
 2         vector<string> s;
 3         dfs(0,0,"",s,n);
 4         return s;
 5     }
 6     void dfs(int left, int right, string tmp, vector<string> & s, int n)
 7     {
 8         if(left==n&&right==n)
 9         {
10             s.push_back(tmp);
11             return;
12         }
13         if(left<n)
14             dfs(left+1,right,tmp+'(',s,n);
15         if(left>right&&right<n)
16             dfs(left,right+1,tmp+')',s,n);
17     }

 substring-with-concatenation-of-all-words

 这道题空间换时间的思路特别,不同word concate的组合非常多,枚举非常不明智,那么就hash好了。接下来就是暴力匹配,内部循环words.size()次,用另一个hash map记录匹配到的word个数。

O(n)方法,尺取法,每次尺取一个word长度,还是两个hashmap

 1 vector<int> findSubstring(string S, vector<string>& L) {
 2         if (S.empty() || L.empty())return {};
 3         vector<int> res;
 4         int n = S.size(), cnt = L.size(), len = L[0].size();
 5         unordered_map<string, int> m1;
 6         for (string w : L) ++m1[w];
 7         for (int i = 0; i < len; i++)
 8         {
 9             int left = i, count = 0;
10             unordered_map<string, int> m2;
11             for (int j = i; j <= n - len; j += len)
12             {
13                 string t = S.substr(j,len);
14                 if (m1.count(t))
15                 {
16                     ++m2[t];
17                     if (m2[t] <= m1[t])
18                         ++count;
19                     else
20                     {
21                         while (m2[t] > m1[t])
22                         {
23                             string tmp = S.substr(left, len);
24                             m2[tmp]--;
25                             if (m2[tmp] < m1[tmp])count--;
26                             left += len;
27                         }
28                     }
29                     if (count == cnt)
30                     {
31                         res.push_back(left);
32                         m2[S.substr(left, len)]--;
33                         --count;
34                         left += len;
35                     }
36                 }
37                 else
38                 {
39                     m2.clear();
40                     count = 0;
41                     left = j + len;
42                 }
43             }
44         }
45         sort(res.begin(),res.end());
46         return res;
47     }

 longest-valid-parentheses

找出最长的合法括号子串,输出长度

常规做法,栈中存左括号,栈空时记录起始节点位置

 1 int longestValidParentheses(string s) {
 2         stack<int> st;
 3         int ans=0,t=-1;
 4         if(s.size()==0)return 0;
 5         for(int i=0;i<s.size();i++)
 6         {
 7             if(s[i]=='(')st.push(i);
 8             else
 9             {
10                 if(st.empty())t=i;
11                 else
12                 {
13                     st.pop();
14                     if(st.empty())
15                         ans = max(ans,i-t);
16                     else
17                         ans  = max(ans,i-st.top());
18                 }
19             }
20         }
21         return ans;
22     }

 Largest Rectangle in Histogram

 1 class Solution {
 2 public:
 3     int largestRectangleArea(vector<int>& heights) {
 4         int res = 0;
 5         stack<int> st;
 6         heights.push_back(0);
 7         for (int i = 0; i < heights.size(); ++i) {
 8             while (!st.empty() && heights[st.top()] >= heights[i]) {
 9                 int cur = st.top(); st.pop();
10                 res = max(res, heights[cur] * (st.empty() ? i : (i - 1 - st.top())));// 注意这里是st.top()而不是cur,两者之间有可行的宽度,前一次出栈了
11             }
12             st.push(i);
13         }
14         return res;
15     }
16 };

 Longest Consecutive Sequence 求最长连续序列

O(n)时间复杂度

方法(1) hash set求解,将序列存入set,对每个数,从左右两个方向展开搜索hash匹配,然后删除这个数,避免重复搜索

方法(2) hash map求解,map初始为空,如果map[x]有值则表示已访问,continue(这里强调用count判断,如果用默认map映射判断,会自动生成一个为0的映射),只需判断map[x-1], map[x+1]即可,更新ma[x-left], map[x+right]的值。

 1 class Solution {
 2 public:
 3     int longestConsecutive(vector<int>& nums) {
 4         int res = 0;
 5         unordered_map<int, int> m;
 6         for (int num : nums) {
 7             if (m.count(num)) continue;
 8             int left = m.count(num - 1) ? m[num - 1] : 0;
 9             int right = m.count(num + 1) ? m[num + 1] : 0;
10             int sum = left + right + 1;
11             m[num] = sum;
12             res = max(res, sum);
13             m[num - left] = sum;
14             m[num + right] = sum;
15         }
16         return res;
17     }
18 };

 Course schedule(拓扑排序)

方法一:bfs+入度

方法二:dfs+访问标记(标记循环) 0表示还未访问过,1表示已经访问了(为了剪枝),-1 表示有冲突(一次dfs中判断是否有环)

 1 class Solution {
 2 public:
 3     bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
 4         vector<vector<int>> graph(numCourses, vector<int>());
 5         vector<int> visit(numCourses);
 6         for (auto a : prerequisites) {
 7             graph[a[1]].push_back(a[0]);
 8         }
 9         for (int i = 0; i < numCourses; ++i) {
10             if (!canFinishDFS(graph, visit, i)) return false;
11         }
12         return true;
13     }
14     bool canFinishDFS(vector<vector<int>>& graph, vector<int>& visit, int i) {
15         if (visit[i] == -1) return false;
16         if (visit[i] == 1) return true;
17         visit[i] = -1;
18         for (auto a : graph[i]) {
19             if (!canFinishDFS(graph, visit, a)) return false;
20         }
21         visit[i] = 1;
22         return true;
23     }
24 };

 Sliding Window Maximum

1 最大堆 pair<value, position> 2 双端队列,维护按序单调递减

 1 class Solution {
 2 public:
 3     vector<int> maxSlidingWindow(vector<int>& nums, int k) {
 4         vector<int> res;
 5         deque<int> q;
 6         for (int i = 0; i < nums.size(); ++i) {
 7             if (!q.empty() && q.front() == i - k) q.pop_front(); // 删除左边元素
 8             while (!q.empty() && nums[q.back()] < nums[i]) q.pop_back();// 保持队列单调递减
 9             q.push_back(i);
10             if (i >= k - 1) res.push_back(nums[q.front()]);
11         }
12         return res;
13     }
14 };

 Meeting Rooms II 

计算线段最大重叠个数,对于线段,可以特殊处理,起始点+1,终止点-1,这样遍历的过程可以模拟实线段

 1 class Solution {
 2 public:
 3     int minMeetingRooms(vector<Interval>& intervals) {
 4         map<int, int> m;
 5         for (auto a : intervals) {
 6             ++m[a.start];
 7             --m[a.end];
 8         }
 9         int rooms = 0, res = 0;
10         for (auto it : m) {
11             res = max(res, rooms += it.second);
12         }
13         return res;
14     }
15 };

 LRU cache

链表 和 map 实现 LRU 内存换页

get 和 put, get 函数是通过输入 key 来获得 value,如果成功获得后, (key, value) 升至缓存器中最常用的位置(顶部),如果 key 不存在,则返回 -1。put 函数是插入一对新的 (key, value),如果原缓存器中有该 key,则需要先删除掉原有的,将新的插入到缓存器的顶部。如果不存在,则直接插入到顶部。若加入新的值后缓存器超过了容量,则需要删掉一个最不常用底部的值。

 1 class LRUCache {
 2 public:
 3     LRUCache(int capacity) {
 4         size = capacity;
 5     }
 6 
 7     int get(int key) {
 8         auto it = hash.find(key);
 9         if(it == hash.end()) return -1;
10         cache.splice(cache.begin(), cache, it->second);
11         return it->second->second;
12     }
13 
14     void put(int key, int value) {
15         auto it = hash.find(key);
16         if(it != hash.end()){
17             it->second->second = value;
18             return cache.splice(cache.begin(), cache, it->second);
19         }
20         cache.insert(cache.begin(), make_pair(key, value));
21         hash[key] = cache.begin();
22         if(cache.size() > size){
23             hash.erase(cache.back().first);
24             cache.pop_back();
25         }
26     }
27 private:
28     unordered_map<int, list<pair<int, int>>::iterator> hash;
29     list<pair<int, int>> cache;
30     int size;
31 };
32 
33 /**
34  * Your LRUCache object will be instantiated and called as such:
35  * LRUCache obj = new LRUCache(capacity);
36  * int param_1 = obj.get(key);
37  * obj.put(key,value);
38  */

 

 python

 1 class Node:
 2 def __init__(self, k, v):
 3     self.key = k
 4     self.val = v
 5     self.prev = None
 6     self.next = None
 7 
 8 class LRUCache:
 9 def __init__(self, capacity):
10     self.capacity = capacity
11     self.dic = dict()
12     self.head = Node(0, 0)
13     self.tail = Node(0, 0)
14     self.head.next = self.tail
15     self.tail.prev = self.head
16 
17 def get(self, key):
18     if key in self.dic:
19         n = self.dic[key]
20         self._remove(n)
21         self._add(n)
22         return n.val
23     return -1
24 
25 def set(self, key, value):
26     if key in self.dic:
27         self._remove(self.dic[key])
28     n = Node(key, value)
29     self._add(n)
30     self.dic[key] = n
31     if len(self.dic) > self.capacity:
32         n = self.head.next
33         self._remove(n)
34         del self.dic[n.key]
35 
36 def _remove(self, node):
37     p = node.prev
38     n = node.next
39     p.next = n
40     n.prev = p
41 
42 def _add(self, node):
43     p = self.tail.prev
44     p.next = node
45     self.tail.prev = node
46     node.prev = p
47     node.next = self.tail

Coin Change 

DP有两种写法,一种为自底向上(dp迭代),另一种为自顶向下(dfs+记忆数组),两者等价

 1 class Solution {
 2 public:
 3     int coinChange(vector<int>& coins, int amount) {
 4         vector<int> memo(amount+1,INT_MAX);
 5         memo[0]=0;
 6         return dfs(coins,amount,memo);
 7     }
 8     int dfs(vector<int>& coins, int target, vector<int>& memo){
 9         if(target<0)return -1;
10         if(memo[target]!=INT_MAX)return memo[target];
11         for(auto coin : coins){
12             int tmp = dfs(coins, target - coin, memo);
13             if(tmp>=0) memo[target] = min(memo[target], tmp+1);
14         }
15         return memo[target] = (memo[target] == INT_MAX) ? -1 : memo[target];
16     }
17 };

 

Validate IP Address 验证IP地址

注意getline的用法何stoi的用法很省事

getline(is, t, '.')

https://www.cnblogs.com/grandyang/p/6185339.html

 

Combination Sum II 组合之和之二

注意sort后

 if (i > start && num[i] == num[i - 1]) continue;

https://www.cnblogs.com/grandyang/p/4419386.html 

 

两个有序数组中位数

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    int n = nums1.length;
    int m = nums2.length;
    int left = (n + m + 1) / 2;
    int right = (n + m + 2) / 2;
    //将偶数和奇数的情况合并,如果是奇数,会求两次同样的 k 。
    return (getKth(nums1, 0, n - 1, nums2, 0, m - 1, left) + getKth(nums1, 0, n - 1, nums2, 0, m - 1, right)) * 0.5;  
}
    
    private int getKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k) {
        int len1 = end1 - start1 + 1;
        int len2 = end2 - start2 + 1;
        //让 len1 的长度小于 len2,这样就能保证如果有数组空了,一定是 len1 
        if (len1 > len2) return getKth(nums2, start2, end2, nums1, start1, end1, k);
        if (len1 == 0) return nums2[start2 + k - 1];

        if (k == 1) return Math.min(nums1[start1], nums2[start2]);

        int i = start1 + Math.min(len1, k / 2) - 1;
        int j = start2 + Math.min(len2, k / 2) - 1;

        if (nums1[i] > nums2[j]) {
            return getKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));
        }
        else {
            return getKth(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1));
        }
    }

 

posted @ 2018-09-23 13:05  demianzhang  阅读(778)  评论(0编辑  收藏  举报