LeetCode OJ 题解
博客搬至blog.csgrandeur.com,cnblogs不再更新。
新的题解会更新在新博客:http://blog.csgrandeur.com/3/
————————————————————————————————————————
————————————————————————————————————————
LeetCode OJ 题解
LeetCode OJ is a platform for preparing technical coding interviews.
LeetCode OJ 是为与写代码有关的技术工作面试者设计的训练平台。
LeetCode OJ:http://oj.leetcode.com/
默认题目顺序为题目添加时间倒叙,本文题解顺序与OJ题目顺序一致(OJ会更新,至少目前一致。。。),目前共152题。
Made By:CSGrandeur
另外,Vimer做了Python版的题解:http://c4fun.cn/blog/2014/03/20/leetcode-solution-02/
————————————————————————————————————————
Maximum Product Subarray
维护当前位置连续乘积的最大值 tmpp 和最小值 tmpn ,最大值和最小值都可能由三种情况得到:上一个数的 tmpp*A[i],上一个数的 tmpn*A[i],A[i]本身。
不断更新答案,最终输出。
1 class Solution { 2 public: 3 int maxProduct(int A[], int n) { 4 int tmpp = A[0], tmpn = A[0], tmp, ans = A[0]; 5 for(int i = 1; i < n; i ++) 6 { 7 tmp = tmpp; 8 tmpp = max(max(A[i], A[i] * tmpp), A[i] * tmpn); 9 tmpn = min(min(A[i], A[i] * tmp), A[i] * tmpn); 10 ans = max(ans, tmpp); 11 } 12 return ans; 13 } 14 };
先翻转整个字符串,然后从前往后一个单词一个单词地再翻转一次,同时去除多余空格,等于是扫描两遍,O(n)。
1 class Solution { 2 public: 3 void reverseWords(string &s) { 4 reverse(s.begin(), s.end()); 5 int start = 0, end = 0, j = 0; 6 while(start != s.length()) 7 { 8 while(start != s.length() && s[start] == ' ') start ++; 9 for(end = start; end != s.length() && s[end] != ' '; end ++); 10 if(j != 0 && start <= end - 1) s[j ++] = ' '; 11 for(int i = end - 1; start < i; start ++, i --) 12 swap(s[i], s[start]), s[j ++] = s[start]; 13 while(start < end) s[j ++] = s[start ++]; 14 } 15 s.resize(j); 16 } 17 };
Evaluate Reverse Polish Notation
逆波兰表达式计算四则运算。用栈。
1 class Solution { 2 public: 3 int evalRPN(vector<string> &tokens) { 4 int a, b; 5 stack<int> s; 6 for(int i = 0; i < tokens.size(); i ++) 7 { 8 if(isdigit(tokens[i][0]) || tokens[i].length() > 1) 9 { 10 s.push(atoi(tokens[i].c_str())); 11 continue; 12 } 13 a = s.top();s.pop(); 14 b = s.top();s.pop(); 15 switch(tokens[i][0]) 16 { 17 case '+': s.push(b + a); break; 18 case '-': s.push(b - a); break; 19 case '*': s.push(b * a); break; 20 case '/': s.push(b / a); break; 21 } 22 } 23 return s.top(); 24 } 25 };
Max Points on a Line
平面上一条直线最多穿过多少点。乍一看好熟悉的问题,做了这么久计算几何。。。却还真没做过这个小问题。
第一反应当然不能O(n^3)枚举了,枚举圆周好像也不行,毕竟是考察所有点,不是某个点。那么应该就是哈希斜率了吧。
肯定少不了竖直的线,哈希斜率这不像是这类OJ让写的题吧。。忘了map这回事了。
确定思路之后,还是看看别人博客吧,少走点弯路,然后就学习了还有unordered_map这么个东西,还有一个博客的思路挺好,避免double问题,把斜率转化成化简的x、y组成字符串。
再另外就是重叠的点了,想让题目坑一点,怎能少得了这种数据,单独处理一下。
1 /** 2 * Definition for a point. 3 * struct Point { 4 * int x; 5 * int y; 6 * Point() : x(0), y(0) {} 7 * Point(int a, int b) : x(a), y(b) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 int maxPoints(vector<Point> &points) { 13 int ans = 0; 14 for(int i = 0; i < points.size(); i ++) 15 { 16 unordered_map<string, int> mp; 17 int tmpans = 0, same = 0; 18 for(int j = i + 1; j < points.size(); j ++) 19 { 20 int x = points[j].x - points[i].x, y = points[j].y - points[i].y; 21 int g = gcd(x, y); 22 if(g != 0) x /= g, y /= g; 23 else {same ++; continue;} 24 if(x < 0) x = -x, y = -y; 25 string tmp = to_string(x) + " " + to_string(y); 26 if(!mp.count(tmp)) mp[tmp] = 1; 27 else mp[tmp] ++; 28 tmpans = max(tmpans, mp[tmp]); 29 } 30 ans = max(tmpans + 1 + same, ans); 31 } 32 return ans; 33 } 34 int gcd(int a, int b) 35 { 36 return a ? gcd(b % a, a) : b; 37 } 38 };
Sort List
又长见识了,原来链表也可以O(nlogn)排序的。没往下想就查了一下,看到人说用归并,于是才开始想能不能实现。。。
O(n)找到中点,把中点的next变成NULL,对两部分递归。递归结束后对两部分归并,先找到newhead,即两部分的头部val较小的那个,然后归并就把小的从newhead往后续。
把最后的next赋值NULL,返回newhead。
又有空数据@_@.
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 *sortList(ListNode *head) { 12 int n = 0; 13 ListNode *p = head; 14 while(p != NULL) 15 n ++, p = p->next; 16 if(n <= 1) return head; 17 n >>= 1; 18 p = head; 19 while(-- n) 20 p = p->next; 21 ListNode *tmp = p->next; 22 p->next = NULL; 23 ListNode *nl = sortList(head); 24 ListNode *nr = sortList(tmp); 25 ListNode *newhead; 26 if(nl->val < nr->val) 27 { 28 newhead = nl; 29 nl = nl->next; 30 } 31 else 32 { 33 newhead = nr; 34 nr = nr->next; 35 } 36 p = newhead; 37 while(nl != NULL && nr != NULL) 38 { 39 if(nl->val < nr->val) p->next = nl, p = p->next, nl = nl->next; 40 else p->next = nr, p = p->next, nr = nr->next; 41 } 42 while(nl != NULL) p->next = nl, p = p->next, nl = nl->next; 43 while(nr != NULL) p->next = nr, p = p->next, nr = nr->next; 44 p->next = NULL; 45 return newhead; 46 } 47 };
Insertion Sort List
指针操作很烦啊。。暴力枚举插入位置,注意细节就能过了。
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 *insertionSortList(ListNode *head) { 12 ListNode *newhead = head; 13 if(head == NULL) return NULL; 14 head = head->next; 15 newhead->next = NULL; 16 while(head != NULL) 17 { 18 if(head->val < newhead->val) 19 { 20 ListNode *tmp = head->next; 21 head->next = newhead; 22 newhead = head; 23 head = tmp; 24 continue; 25 } 26 ListNode *pre = newhead, *p = newhead->next; 27 while(p != NULL && p->val < head->val) 28 { 29 p = p->next; 30 pre = pre->next; 31 } 32 pre->next = head; 33 head = head->next; 34 pre = pre->next; 35 pre->next = p; 36 } 37 return newhead; 38 } 39 40 };
新建数据类class Val,保存key、val和访问自增标记updatecnt。
用unordered_map更新数据,增加updatecnt,并把更新的数据放入队列,最关键是处理capacity满了的时候,队列依次出队,map中不存在的和updatecnt和最新数据不相等的项目都忽略,直到发现updatecnt和map中存的最新状态相等,则为“最近未使用”数据,出队后在map中erase。思路有点像STL队列实现版本的Dijkstra。
有一个博客的方法更好,map中存的是链表的节点指针,链表顺序表示访问情况,这样就把map内容和链表的每个节点一一对应了,没有冗余节点,且更新操作也是O(1)的。
1 class Val{ 2 public: 3 int key; 4 int val; 5 int updatecnt; 6 }; 7 class LRUCache{ 8 public: 9 int cap; 10 unordered_map<int, Val> mp; 11 queue<Val> q; 12 LRUCache(int capacity) { 13 cap = capacity; 14 } 15 16 int get(int key) { 17 if(mp.count(key)) 18 { 19 mp[key].updatecnt ++; 20 q.push(mp[key]); 21 return mp[key].val; 22 } 23 return -1; 24 } 25 26 void set(int key, int value) { 27 if(mp.count(key)) 28 { 29 mp[key].val = value; 30 mp[key].updatecnt ++; 31 q.push(mp[key]); 32 } 33 else 34 { 35 if(mp.size() == cap) 36 { 37 Val tmp; 38 while(!q.empty()) 39 { 40 tmp = q.front(); 41 q.pop(); 42 if(mp.count(tmp.key) && tmp.updatecnt == mp[tmp.key].updatecnt) 43 break; 44 } 45 mp.erase(mp.find(tmp.key)); 46 mp[key].key = key; 47 mp[key].val = value; 48 mp[key].updatecnt = 0; 49 q.push(mp[key]); 50 } 51 mp[key].key = key; 52 mp[key].val = value; 53 mp[key].updatecnt = 0; 54 q.push(mp[key]); 55 } 56 } 57 };
Binary Tree Postorder Traversal
二叉树的非递归后序遍历,考研的时候非常熟悉了,现在写又要想好久。重点是关于右子树遍历时候需要一个标记,或者标记根节点出栈次数,或者标记右子树是否访问。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<int> postorderTraversal(TreeNode *root) { 13 vector<int> ans; 14 if(root == NULL) return ans; 15 stack<TreeNode*> s; 16 TreeNode *visited; 17 while(root != NULL || !s.empty()) 18 { 19 while(root != NULL) 20 s.push(root), root = root->left; 21 root = s.top(); 22 if(root->right == NULL || visited == root->right) 23 { 24 ans.push_back(root->val); 25 s.pop(); 26 visited = root; 27 root = NULL; 28 } 29 else 30 { 31 root = root->right; 32 } 33 } 34 return ans; 35 } 36 };
Binary Tree Preorder Traversal
前序遍历的非递归就容易多了。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<int> preorderTraversal(TreeNode *root) { 13 stack<TreeNode*> s; 14 vector<int> ans; 15 if(root == NULL) return ans; 16 s.push(root); 17 while(!s.empty()) 18 { 19 root = s.top(); 20 s.pop(); 21 ans.push_back(root->val); 22 if(root->right != NULL) s.push(root->right); 23 if(root->left != NULL) s.push(root->left); 24 } 25 } 26 };
Reorder List
找到中间位置,把中间之后的链表反转,两个指针一个从头一个从尾开始互插,奇偶和指针绕得有点晕,理清就好了。。
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 void reorderList(ListNode *head) { 12 int n = 0; 13 ListNode *pre, *p = head; 14 while(p) 15 n ++, p = p->next; 16 if(n < 3) return; 17 n >>= 1; 18 pre = p = head; 19 p = p->next; 20 while(n --) p = p->next, pre = pre->next; 21 while(p != NULL) 22 { 23 ListNode *tmp = p->next; 24 p->next = pre; 25 pre = p; 26 p = tmp; 27 } 28 ListNode *tail = pre; 29 p = head; 30 while(true) 31 { 32 ListNode *tmp1 = p->next, *tmp2 = tail->next; 33 p->next = tail; 34 tail->next = tmp1; 35 p = tmp1; 36 if(p == tail || p == tmp2) break; 37 tail = tmp2; 38 } 39 p->next = NULL; 40 } 41 };
设置两个指针slow和fast,从head开始,slow一次一步,fast一次两步,如果fast能再次追上slow则有圈。
设slow走了n步,则fast走了2*n步,设圈长度m,圈起点到head距离为k,相遇位置距离圈起点为t,则有:
n = k + t + pm; (1)
2*n = k + t + qm;(2)
这里p和q是任意整数。(不过fast速度是slow二倍,则肯定在一圈内追上,p就是0了)
2 * (1) - (2) 得k = lm - t;(l = q - 2 * p)
即 k 的长度是若干圈少了 t 的长度。
因此这时候,一个指针从head开始,另一个从相遇位置开始,都每次只走一步,当从head开始的指针走到圈开始位置时,两指针刚好相遇。
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 *detectCycle(ListNode *head) { 12 if(head == NULL) return NULL; 13 ListNode *slow, *fast; 14 slow = fast = head; 15 int n = 0; 16 do 17 { 18 n ++; 19 if(slow == NULL || fast == NULL) return NULL; 20 slow = slow->next; 21 fast = fast->next; 22 if(fast == NULL) return NULL; 23 fast = fast->next; 24 if(fast == NULL) return NULL; 25 }while(slow != fast); 26 fast = head; 27 while(slow != fast) 28 slow = slow->next, fast = fast->next; 29 return fast; 30 } 31 };
Linked List Cycle
呃,时间逆序做的结果。。。成买一送一了。
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 bool hasCycle(ListNode *head) { 12 if(head == NULL) return false; 13 ListNode *slow, *fast; 14 slow = fast = head; 15 int n = 0; 16 do 17 { 18 n ++; 19 if(slow == NULL || fast == NULL) return NULL; 20 slow = slow->next; 21 fast = fast->next; 22 if(fast == NULL) return NULL; 23 fast = fast->next; 24 if(fast == NULL) return NULL; 25 }while(slow != fast); 26 return true; 27 } 28 };
Word Break II
先递推,dp[i] == true 表示 s 中前 i 个字符的串是符合要求的,枚举位置 i ,对于 i 枚举位置 j < i,如果 dp[j] == true且 j ~ i的串在字典中,则dp[i] = true。
同时对于这样的 j, i,site[i].push_back(j),即在 i 位置的可行迭代表中增加位置 j。
完成site之后,从尾部倒着DFS过去就得到了所有串。
1 class Solution { 2 public: 3 vector<string> DFS(const string &s, vector<int> *site, int ith) 4 { 5 vector<string> res; 6 for(int i = 0; i < site[ith].size(); i ++) 7 { 8 vector<string> tmp; 9 string str = s.substr(site[ith][i], ith - site[ith][i]); 10 if(site[site[ith][i]].size() == 0) 11 res.push_back(str); 12 else 13 { 14 tmp = DFS(s, site, site[ith][i]); 15 for(int j = 0; j < tmp.size(); j ++) 16 res.push_back(tmp[j] + " " + str); 17 } 18 } 19 return res; 20 } 21 vector<string> wordBreak(string s, unordered_set<string> &dict) { 22 vector<int> *site = new vector<int>[s.length() + 1]; 23 bool *dp = new bool[s.length() + 1]; 24 memset(dp, 0, sizeof(bool) * s.length()); 25 dp[0] = true; 26 for(int i = 1; i <= s.length(); i ++) 27 { 28 for(int j = 0; j < i; j ++) 29 { 30 if(dp[j] == true && dict.count(s.substr(j, i - j))) 31 site[i].push_back(j), dp[i] = true; 32 } 33 } 34 return DFS(s, site, s.length()); 35 } 36 };
Word Break
参考Word Break II,对于dp标记,当dp[i]为true时候可以停止枚举后面的 j,优化一下常数。
1 class Solution { 2 public: 3 bool wordBreak(string s, unordered_set<string> &dict) { 4 bool *dp = new bool[s.length() + 1]; 5 memset(dp, 0, sizeof(bool) * (s.length() + 1)); 6 dp[0] = true; 7 for(int i = 1; i <= s.length(); i ++) 8 for(int j = 0; j < i; j ++) 9 { 10 dp[i] = dp[i] || dp[j] && dict.count(s.substr(j, i - j)); 11 } 12 return dp[s.length()]; 13 } 14 };
Copy List with Random Pointer
第一次遍历,把每个节点复制一份放到对应节点的下一个,即组成二倍长的链表:ori1->copy1->ori2->copy2->....
第二次遍历,利用“复制节点总是对应节点的下一个节点”特性,将每个ori->next->random指向ori->random->next,中间判断一下空指针。
第三次遍历,把两个链表拆开,恢复原链表。
1 /** 2 * Definition for singly-linked list with a random pointer. 3 * struct RandomListNode { 4 * int label; 5 * RandomListNode *next, *random; 6 * RandomListNode(int x) : label(x), next(NULL), random(NULL) {} 7 * }; 8 */ 9 class Solution { 10 public: 11 RandomListNode *copyRandomList(RandomListNode *head) { 12 RandomListNode *p = head, *newhead = NULL, *tmp; 13 if(p == NULL) return NULL; 14 while(p != NULL) 15 { 16 tmp = new RandomListNode(p->label); 17 tmp->next = p->next; 18 p->next = tmp; 19 p = tmp->next; 20 } 21 newhead = head->next; 22 p = head; 23 while(p != NULL) 24 { 25 tmp = p->next; 26 tmp->random = p->random == NULL ? NULL : p->random->next; 27 p = tmp->next; 28 } 29 p = head; 30 while(p != NULL) 31 { 32 tmp = p->next; 33 p->next = tmp->next; 34 p = tmp->next; 35 tmp->next = p == NULL ? NULL : p->next; 36 } 37 return newhead; 38 } 39 };
Single Number II
方法一:设置cnt[32]记录 32个比特位的1的个数,出现3次的数的对应位的1总数为3的倍数,则统计之后每个位对3取模,剩下的位为1的则对应个数为1的那个数。
1 class Solution { 2 public: 3 int singleNumber(int A[], int n) { 4 int cnt[32] = {0}; 5 for(int i = 0; i < n; i ++) 6 { 7 int tmp = A[i]; 8 for(int j = 0; j < 33; tmp >>= 1, j ++) 9 cnt[j] += tmp & 1; 10 } 11 int ans = 0; 12 for(int i = 0; i < 32; i ++) 13 ans |= (cnt[i] % 3) << i; 14 return ans; 15 } 16 };
方法二:设置int one, two模拟两位二进制来统计各比特位1次数,每当one和two对应二进制位都为1的时候把one和two都清零,最后剩下的one就是要求的数。
1 class Solution { 2 public: 3 int singleNumber(int A[], int n) { 4 int one = 0, two = 0; 5 for(int i = 0; i < n; i ++) 6 { 7 two |= one & A[i]; 8 one ^= A[i]; 9 int tmp = one & two; 10 two ^= tmp; 11 one ^= tmp; 12 } 13 return one; 14 } 15 };
一路异或过去就可以了。
1 class Solution { 2 public: 3 int singleNumber(int A[], int n) { 4 int tmp = 0; 5 for(int i = 0; i < n; i ++) 6 tmp ^= A[i]; 7 return tmp; 8 } 9 };
Candy
时间复杂度 O(n)的方法还是容易想了,优化为空间复杂度O(1)的话也不难,只是思考代码的时候会有点绕。
上坡一步步来,下坡走个等差数列,波峰位置比较一下上坡时候记录的最大值和下坡求的的最大值,取较大的,具体看代码:
1 class Solution { 2 public: 3 int candy(vector<int> &ratings) { 4 int cnt = 0, i, j, start, nownum; 5 for(i = 0; i < ratings.size(); i ++) 6 { 7 if(i == 0 || ratings[i] == ratings[i - 1]) 8 nownum = 1; 9 else if(ratings[i] > ratings[i - 1]) 10 nownum ++; 11 if(i + 1 < ratings.size() && ratings[i + 1] < ratings[i]) 12 { 13 start = 1; 14 for(j = i + 1; j < ratings.size() && ratings[j] < ratings[j - 1]; start++, j ++); 15 if(start > nownum) 16 cnt += (start + 1) * start >> 1; 17 else 18 cnt += ((start - 1) * start >> 1) + nownum; 19 nownum = 1; 20 i = j - 1; 21 } 22 else 23 cnt += nownum; 24 } 25 return cnt; 26 } 27 };
Gas Station
证明题。
一、如果从 i 到 j 的时候理论计算气量刚好为负数,则 i ~ j 的加气站都不可以作为起点。
反证一下,从前往后去掉站,如果去掉的站能增加气,即正数,则结果更糟。如果去掉的站是负数,那么负数如果抵消了之前的正数,则在到 j 之前已经负数了,如果不能抵消之前的正数,那么结果还是更糟。
二、判断是否能成行,一个环的和为非负就可以。
假设环为正, 0 ~ j 刚好为负, j + 1 ~ k 刚好为负数,k + 1 之后为正,则 k + 1 为答案。
也反证一下,k + 1 出发,到gas.size() - 1都为正,则转回来到 j - 1 都会为正。如果到 j 时候为负,则整个环不可能为正,所以到 j 的时候也为正,剩下的一样。这样就能够成功转一圈。
1 class Solution { 2 public: 3 int canCompleteCircuit(vector<int> &gas, vector<int> &cost) { 4 int i, ans, sum, all; 5 for(i = ans = sum = all = 0; i < gas.size(); i ++) 6 { 7 sum += gas[i] - cost[i]; 8 all += gas[i] - cost[i]; 9 if(sum < 0) 10 { 11 sum = 0; 12 ans = i + 1; 13 } 14 } 15 return all >= 0 ? ans : -1; 16 } 17 };
Clone Graph
label是唯一的,递归,用unordered_map标记。
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 unordered_map<int, UndirectedGraphNode *> mp; 12 UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) { 13 if(node == NULL || mp.count(node->label)) return NULL; 14 UndirectedGraphNode *tmp = new UndirectedGraphNode(node->label); 15 mp[node->label] = tmp; 16 for(int i = 0; i < node->neighbors.size(); i ++) 17 { 18 cloneGraph(node->neighbors[i]); 19 tmp->neighbors.push_back(mp[node->neighbors[i]->label]); 20 } 21 return tmp; 22 } 23 };
Palindrome Partitioning II
O(n^2)的动态规划。
cutdp[i] 表示前 i 个字符最小切割几次。
paldp[i][j] == true 表示 i ~ j 是回文。
在枚举 i 和 i 之前的所有 j 的过程中就得到了 paldp[j][i] 的所有回文判断,而对于 i + 1,paldp[j][i + 1]可由 s[j]、s[i + 1]、dp[j + 1][i]在O(1)判断。
cutdp[i]为所有 j (j < i),当paldp[j + 1][i] == true的 cutdp[j] + 1的最小值。注意一下边界。
1 class Solution { 2 public: 3 int minCut(string s) { 4 bool paldp[s.length()][s.length()]; 5 int cutdp[s.length()]; 6 for(int i = 0; i < s.length(); i ++) 7 { 8 cutdp[i] = 0x3f3f3f3f; 9 for(int j = i - 1; j >= -1; j --) 10 { 11 if(s.at(j + 1) == s.at(i) && (j + 2 >= i - 1 || paldp[j + 2][i - 1])) 12 { 13 paldp[j + 1][i] = true; 14 cutdp[i] = min(cutdp[i], (j >= 0 ? (cutdp[j] + 1) : 0)); 15 } 16 else 17 paldp[j + 1][i] = false; 18 19 } 20 } 21 return cutdp[s.length() - 1]; 22 } 23 };
Palindrome Partitioning
O(n^2)动态规划,paldp[i][j] == true表示 i ~ j 是回文。这里DP的方法是基本的,不再多说。
得到paldp之后,DFS一下就可以了。因为单字符是回文,所以DFS的终点肯定都是解,所以不必利用其他的结构存储答案信息。
1 class Solution { 2 public: 3 vector<vector<string> >res; 4 vector<string> tmp; 5 bool **paldp; 6 void DFS(string s, int ith) 7 { 8 if(ith == s.length()) 9 { 10 res.push_back(tmp); 11 return; 12 } 13 for(int i = ith; i < s.length(); i ++) 14 { 15 if(paldp[ith][i]) 16 { 17 tmp.push_back(s.substr(ith, i - ith + 1)); 18 DFS(s, i + 1); 19 tmp.pop_back(); 20 } 21 } 22 return; 23 } 24 vector<vector<string> > partition(string s) { 25 paldp = new bool*[s.length()]; 26 for(int i = 0; i < s.length(); i ++) 27 paldp[i] = new bool[s.length()]; 28 for(int i = 0; i < s.length(); i ++) 29 for(int j = i; j >= 0; j --) 30 paldp[j][i] = s.at(i) == s.at(j) && (j + 1 >= i - 1 || paldp[j + 1][i - 1]); 31 DFS(s, 0); 32 return res; 33 } 34 };
Surrounded Regions
周围四条边的O做起点搜索替换为第三种符号,再遍历所有符号把O换成X,第三种符号换回O。
1 class Solution { 2 public: 3 typedef pair<int, int> pii; 4 int dx[4] = {1, -1, 0, 0}; 5 int dy[4] = {0, 0, 1, -1}; 6 queue<pii> q; 7 void solve(vector<vector<char> > &board) { 8 if(board.size() == 0) return; 9 int width = board[0].size(); 10 int height = board.size(); 11 for(int i = 0; i < width; i ++) 12 { 13 if(board[0][i] == 'O') 14 board[0][i] = '#', q.push(pair<int, int>(0, i)); 15 if(board[height - 1][i] == 'O') 16 board[height - 1][i] = '#', q.push(pii(height - 1, i)); 17 } 18 for(int i = 1; i < height - 1; i ++) 19 { 20 if(board[i][0] == 'O') 21 board[i][0] = '#', q.push(pii(i, 0)); 22 if(board[i][width - 1] == 'O') 23 board[i][width - 1] = '#', q.push(pii(i, width - 1)); 24 } 25 while(!q.empty()) 26 { 27 pii now = q.front(); 28 q.pop(); 29 for(int i = 0; i < 4; i ++) 30 { 31 int ty = now.first + dx[i]; 32 int tx = now.second + dy[i]; 33 if(tx >= 0 && tx < width && ty >= 0 && ty < height && board[ty][tx] == 'O') 34 { 35 board[ty][tx] = '#'; 36 q.push(pii(ty, tx)); 37 } 38 } 39 } 40 for(int i = 0; i < height; i ++) 41 for(int j = 0; j < width; j ++) 42 { 43 if(board[i][j] == 'O') board[i][j] = 'X'; 44 else if(board[i][j] == '#') board[i][j] = 'O'; 45 } 46 } 47 };
Sum Root to Leaf Numbers
遍历一遍加起来。。。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 int ans; 13 void DFS(TreeNode *now, int tmp) 14 { 15 if(now->left == NULL && now->right == NULL) 16 { 17 ans += tmp * 10 + now->val; 18 return; 19 } 20 if(now->left != NULL) 21 { 22 DFS(now->left, tmp * 10 + now->val); 23 } 24 if(now->right != NULL) 25 { 26 DFS(now->right, tmp * 10 + now->val); 27 } 28 } 29 int sumNumbers(TreeNode *root) { 30 if(root == NULL) return 0; 31 ans = 0; 32 DFS(root, 0); 33 return ans; 34 } 35 };
Longest Consecutive Sequence
方法一:一开始竟然想了并查集,其实绕弯了,多此一举。哈希+并查集,把每个数哈希,枚举每个数看相邻的数在不在数组里,并查集合并,只是并查集的复杂度要比O(1)大一些。
1 class Solution { 2 public: 3 unordered_map<int, int> mp, cnt; 4 int ans = 1; 5 int fa(int i) 6 { 7 i == mp[i] ? i : (mp[i] = fa(mp[i])); 8 } 9 int longestConsecutive(vector<int> &num) { 10 for(int i = 0; i < num.size(); i ++) 11 mp[num[i]] = num[i], cnt[num[i]] = 1; 12 for(int i = 0; i < num.size(); i ++) 13 { 14 if(mp.count(num[i] + 1) && fa(num[i]) != fa(num[i] + 1)) 15 { 16 cnt[fa(num[i] + 1)] += cnt[fa(num[i])]; 17 ans = max(cnt[fa(num[i] + 1)], ans); 18 mp[fa(num[i])] = fa(num[i] + 1); 19 } 20 } 21 return ans; 22 } 23 };
方法二:哈希+枚举相邻数。相邻的数在数组里的话,每个数之多访问一次;相邻的数不在数组里的话,枚举会中断。所以设哈希复杂度为O(1)的话,这个方法是严格的O(n)。
其实这个题的数据挺善良,如果出了2147483647, -2147483648,那还是用long long 稳妥些。
1 class Solution { 2 public: 3 unordered_map<int, bool> vis; 4 int longestConsecutive(vector<int> &num) { 5 int ans = 0; 6 for(int i = 0; i < num.size(); i ++) 7 vis[num[i]] = false; 8 for(int i = 0; i < num.size(); i ++) 9 { 10 if(vis[num[i]] == false) 11 { 12 int cnt = 0; 13 for(int j = num[i]; vis.count(j); j ++, cnt ++) 14 { 15 vis[j] = true; 16 } 17 for(int j = num[i] - 1; vis.count(j); j --, cnt ++) 18 { 19 vis[j] = true; 20 } 21 ans = max(ans, cnt); 22 } 23 } 24 25 return ans; 26 } 27 };
Word Ladder II
用数组类型的队列,BFS过程中记录pre路径,搜完后迭代回去保存路径。
似乎卡了常数,用queue队列,另外存路径的方法超时了。
想更快就双向广搜吧。让我想起了POJ那个八数码。
1 class Node 2 { 3 public: 4 string str; 5 int pace; 6 int pre; 7 Node(){} 8 Node(string s, int pa, int pr) 9 { 10 str = s; 11 pace = pa; 12 pre = pr; 13 } 14 }; 15 class Solution { 16 public: 17 vector<vector<string>> ans; 18 vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) { 19 vector<Node> q; 20 q.push_back(Node(end, 1, -1)); 21 unordered_map<string, int> dis; 22 dis[end] = 1; 23 for(int i = 0; i < q.size(); i ++) 24 { 25 Node now = q[i]; 26 if(dis.count(start) && now.pace >= dis[start]) break; 27 for(int j = 0; j < now.str.length(); j ++) 28 { 29 string tmp = now.str; 30 for(char c = 'a'; c <= 'z'; c ++) 31 { 32 tmp[j] = c; 33 if((dict.count(tmp) || tmp == start) && (!dis.count(tmp) || dis[tmp] == now.pace + 1)) 34 { 35 dis[tmp] = now.pace + 1; 36 q.push_back(Node(tmp, now.pace + 1, i)); 37 } 38 } 39 } 40 } 41 for(int i = q.size() - 1; i >= 0 && q[i].pace == dis[start]; i --) 42 { 43 if(q[i].str == start) 44 { 45 vector<string> tmp; 46 for(int j = i; j != -1; j = q[j].pre) 47 tmp.push_back(q[j].str); 48 ans.push_back(tmp); 49 } 50 } 51 return ans; 52 } 53 };
Word Ladder
直接BFS。
1 class Solution { 2 public: 3 int ladderLength(string start, string end, unordered_set<string> &dict) { 4 typedef pair<string, int> pii; 5 unordered_set<string> flag; 6 queue<pii> q; 7 q.push(pii(start, 1)); 8 while(!q.empty()) 9 { 10 pii now = q.front(); 11 q.pop(); 12 for(int i = 0; i < now.first.length(); i ++) 13 { 14 string tmp = now.first; 15 for(char j = 'a'; j <= 'z'; j ++) 16 { 17 tmp[i] = j; 18 if(tmp == end) return now.second + 1; 19 if(dict.count(tmp) && !flag.count(tmp)) 20 { 21 q.push(pii(tmp, now.second + 1)); 22 flag.insert(tmp); 23 } 24 } 25 } 26 } 27 return 0; 28 } 29 };
做过刘汝佳 白书的人想必都知道ctype.h和isdigit(), isalpha, tolower(), toupper()。
1 class Solution { 2 public: 3 bool valid(char &x) 4 { 5 x = tolower(x); 6 return isdigit(x) || isalpha(x); 7 } 8 bool isPalindrome(string s) { 9 if(s.length() == 0) return true; 10 for(int i = 0, j = s.length() - 1; i < j; i ++, j --) 11 { 12 while(!valid(s[i]) && i < s.length()) i ++; 13 while(!valid(s[j]) && j >= 0) j --; 14 if(i < j && s[i] != s[j]) return false; 15 } 16 return true; 17 } 18 };
Binary Tree Maximum Path Sum
后续遍历,子问题为子树根节点向叶子节点出发的最大路径和。
即 l = DFS(now->left), r = DFS(now->right)。
此时,ans可能是 now->valid,可能是左边一路上来加上now->valid,可能是右边一路上来,也可能是左边上来经过now再右边一路下去,四种情况。
四种情况更新完ans后,now返回上一层只能是 now->valid或左边一路上来或右边一路上来,三种情况。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 int ans; 13 int DFS(TreeNode *now) 14 { 15 if(now == NULL) return 0; 16 int l = max(DFS(now->left), 0); 17 int r = max(DFS(now->right), 0); 18 ans = max(ans, l + r + now->val); 19 return max(l + now->val, r + now->val); 20 } 21 int maxPathSum(TreeNode *root) { 22 if(root == NULL) return 0; 23 ans = -0x3f3f3f3f; 24 DFS(root); 25 return ans; 26 } 27 };
Best Time to Buy and Sell Stock III
前缀pre[i]处理 0 ~ i 买卖一次最优解,后缀suf[i]处理 i ~ prices.size() - 1 买卖一次最优解。
所有位置pre[i] + suf[i]最大值为答案O(n)。
处理最优解的时候是维护前(后)缀prices最小(大)值,与当前prices做差后和前(后)缀最优解比较取最优,O(n)。
总复杂度O(n)。
1 class Solution { 2 public: 3 int maxProfit(vector<int> &prices) { 4 int ans = 0; 5 vector<int> pre(prices.size()), suf(prices.size()); 6 for(int i = 0, mtmp = 0x3f3f3f3f; i < prices.size(); i ++) 7 { 8 mtmp = i ? min(mtmp, prices[i]) : prices[i]; 9 pre[i] = max(prices[i] - mtmp, i ? pre[i - 1] : 0); 10 } 11 for(int i = prices.size() - 1, mtmp = 0; i >= 0; i --) 12 { 13 mtmp = i != prices.size() - 1 ? max(mtmp, prices[i]) : prices[i]; 14 suf[i] = max(mtmp - prices[i], i != prices.size() - 1 ? suf[i + 1] : 0); 15 } 16 for(int i = 0; i < prices.size(); i ++) 17 ans = max(ans, pre[i] + suf[i]); 18 return ans; 19 } 20 };
Best Time to Buy and Sell Stock II
可以买卖多次,把所有上坡差累加即可。
1 class Solution { 2 public: 3 int maxProfit(vector<int> &prices) { 4 int ans = 0; 5 for(int i = 1; i < prices.size(); i ++) 6 { 7 if(prices[i] > prices[i - 1]) 8 ans += prices[i] - prices[i - 1]; 9 } 10 return ans; 11 } 12 };
Best Time to Buy and Sell Stock
维护前(后)缀最小(大)值,和当前prices做差更新答案。
1 class Solution { 2 public: 3 int maxProfit(vector<int> &prices) { 4 int ans = 0; 5 for(int i = prices.size() - 1, mtmp = 0; i >= 0; i --) 6 { 7 mtmp = max(mtmp, prices[i]); 8 ans = max(mtmp - prices[i], ans); 9 } 10 return ans; 11 } 12 };
Triangle
竟然遇到了ACM递推入门题,想必无数ACMer对这题太熟悉了。
从下往上递推,一维数组滚动更新即可。这里懒省事,直接把原数组改了。
1 class Solution { 2 public: 3 int minimumTotal(vector<vector<int> > &triangle) { 4 for(int i = triangle.size() - 2; i >= 0; i --) 5 { 6 for(int j = 0; j < triangle[i].size(); j ++) 7 triangle[i][j] = min(triangle[i][j] + triangle[i + 1][j], triangle[i][j] + triangle[i + 1][j + 1]); 8 } 9 return triangle.size() == 0 ? 0 : triangle[0][0]; 10 } 11 };
Pascal's Triangle II
滚动数组递推,从后往前以便不破坏上一层递推数据。
1 class Solution { 2 public: 3 vector<int> getRow(int rowIndex) { 4 vector<int> ans(rowIndex + 1, 0); 5 ans[0] = 1; 6 for(int i = 0; i <= rowIndex; i ++) 7 { 8 for(int j = i; j >= 0; j --) 9 { 10 ans[j] = (i == 0 || j == 0 || j == i ? 1 : ans[j] + ans[j - 1]); 11 } 12 } 13 return ans; 14 } 15 };
Pascal's Triangle
递推。。
1 class Solution { 2 public: 3 vector<vector<int> > generate(int numRows) { 4 vector<vector<int> > v; 5 for(int i = 0; i < numRows; i ++) 6 { 7 vector<int> tmp; 8 for(int j = 0; j <= i; j ++) 9 { 10 tmp.push_back(i == 0 || j == 0 || j == i ? 1 : v[i - 1][j] + v[i - 1][j - 1]); 11 } 12 v.push_back(tmp); 13 } 14 return v; 15 } 16 };
Populating Next Right Pointers in Each Node II
题目要求空间复杂度O(1),所以递归、队列等传统方法不应该用。
本题可以利用生成的next指针来横向扫描,即得到一层的next指针之后,可以利用这一层的next指针来给下一层的next指针赋值。
1 /** 2 * Definition for binary tree with next pointer. 3 * struct TreeLinkNode { 4 * int val; 5 * TreeLinkNode *left, *right, *next; 6 * TreeLinkNode(int x) : val(x), left(NULL), right(NULL), next(NULL) {} 7 * }; 8 */ 9 class Solution { 10 public: 11 TreeLinkNode *findNext(TreeLinkNode *head) 12 { 13 while(head != NULL && head->left == NULL && head->right == NULL) 14 head = head->next; 15 return head; 16 } 17 void connect(TreeLinkNode *root) { 18 if(root == NULL) return; 19 TreeLinkNode *head, *last, *nexhead; 20 for(head = root; head != NULL; head = nexhead) 21 { 22 head = findNext(head); 23 if(head == NULL) break; 24 if(head->left != NULL) nexhead = head->left; 25 else nexhead = head->right; 26 for(last = NULL; head != NULL; last = head, head = findNext(head->next)) 27 { 28 if(head->left != NULL && head->right != NULL) 29 head->left->next = head->right; 30 if(last == NULL) continue; 31 if(last->right != NULL) 32 last->right->next = head->left != NULL ? head->left : head->right; 33 else 34 last->left->next = head->left != NULL ? head->left : head->right; 35 } 36 } 37 } 38 };
Populating Next Right Pointers in Each Node
不用考虑连续的空指针,就不用额外实现找下一个子树非空节点,比Populating Next Right Pointers in Each Node II 容易处理。
1 /** 2 * Definition for binary tree with next pointer. 3 * struct TreeLinkNode { 4 * int val; 5 * TreeLinkNode *left, *right, *next; 6 * TreeLinkNode(int x) : val(x), left(NULL), right(NULL), next(NULL) {} 7 * }; 8 */ 9 class Solution { 10 public: 11 void connect(TreeLinkNode *root) { 12 if(root == NULL) return; 13 TreeLinkNode *head, *nexhead, *last; 14 for(head = root; head->left != NULL; head = nexhead) 15 { 16 nexhead = head->left; 17 last = NULL; 18 while(head != NULL) 19 { 20 head->left->next = head->right; 21 if(last != NULL) last->right->next = head->left; 22 last = head; 23 head = head->next; 24 } 25 } 26 } 27 };
Distinct Subsequences
典型动态规划。dp[i][j] 表示 T 的前 j 个字符在 S 的前 i 个字符中的解。
对于dp[i + 1][j + 1],由两部分组成:
一、 j + 1 对应到 S 前 i 个字符中的解,忽略 S 的第 i + 1 个字符。
二、判断 S 的第 i + 1 个字符是否和 T 的第 j + 1 个字符相同,如果相同,则加上dp[i][j],否则不加。
1 class Solution { 2 public: 3 int numDistinct(string S, string T) { 4 if(S.length() < T.length()) return 0; 5 vector<vector<int> > dp(S.length() + 1, vector<int>(T.length() + 1, 0)); 6 for(int i = 0; i < S.length(); i ++) dp[i][0] = 1; 7 dp[0][1] = 0; 8 for(int i = 0; i < S.length(); i ++) 9 { 10 for(int j = 0; j < T.length(); j ++) 11 { 12 dp[i + 1][j + 1] = dp[i][j + 1]; 13 if(S[i] == T[j]) dp[i + 1][j + 1] += dp[i][j]; 14 } 15 } 16 return dp[S.length()][T.length()]; 17 } 18 };
Flatten Binary Tree to Linked List
题意是优先左子树靠前,且排成一列用右子树指针,不管val的大小关系。
后序遍历一遍即可,递归返回子树中尾节点指针,注意各种条件判断。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 TreeNode *DFS(TreeNode *now) 13 { 14 if(now->left == NULL && now->right == NULL) return now; 15 TreeNode *leftok = NULL, *rightok = NULL; 16 if(now->left != NULL) leftok = DFS(now->left); 17 if(now->right != NULL) rightok = DFS(now->right); 18 if(leftok != NULL) 19 { 20 leftok->right = now->right; 21 now->right = now->left; 22 now->left = NULL; 23 return rightok ? rightok : leftok; 24 } 25 else return rightok; 26 } 27 void flatten(TreeNode *root) { 28 if(root == NULL) return; 29 DFS(root); 30 } 31 };
传统递归,把路径上的数字插入vector,终点判断是否插入答案。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 int goal; 13 vector<vector<int> >v; 14 vector<int> curv; 15 void DFS(TreeNode *now, int cursum) 16 { 17 curv.push_back(now->val); 18 if(now->left == NULL && now->right == NULL) 19 { 20 if(cursum + now->val == goal) 21 { 22 v.push_back(curv); 23 curv.pop_back(); 24 return; 25 } 26 } 27 if(now->left != NULL) DFS(now->left, cursum + now->val); 28 if(now->right != NULL) DFS(now->right, cursum + now->val); 29 curv.pop_back(); 30 } 31 vector<vector<int> > pathSum(TreeNode *root, int sum) { 32 goal = sum; 33 if(root == NULL) return v; 34 DFS(root, 0); 35 return v; 36 } 37 };
Path Sum
遍历树。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 int goal; 13 bool DFS(TreeNode *now, int cursum) 14 { 15 if(now->left == NULL && now->right == NULL) 16 return cursum + now->val == goal; 17 if(now->left != NULL && DFS(now->left, cursum + now->val)) return true; 18 if(now->right != NULL && DFS(now->right, cursum + now->val)) return true; 19 } 20 bool hasPathSum(TreeNode *root, int sum) { 21 goal = sum; 22 if(root == NULL) return false; 23 return DFS(root, 0); 24 } 25 };
还是遍历。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 int minDepth(TreeNode *root) { 13 if(root == NULL) return 0; 14 if(root->left == NULL) return minDepth(root->right) + 1; 15 else if(root->right == NULL) return minDepth(root->left) + 1; 16 else return min(minDepth(root->left), minDepth(root->right)) + 1; 17 } 18 };
遍历。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 int maxDepth(TreeNode *now) 13 { 14 if(now == NULL) return 0; 15 int l = maxDepth(now->left) + 1; 16 int r = maxDepth(now->right) + 1; 17 return abs(l - r) > 1 || l < 0 || r < 0 ? -2 : max(l, r); 18 } 19 bool isBalanced(TreeNode *root) { 20 return maxDepth(root) >= 0; 21 } 22 };
Convert Sorted List to Binary Search Tree
每次找中点作为根节点,将两边递归,返回根节点指针作为左右节点。
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 /** 10 * Definition for binary tree 11 * struct TreeNode { 12 * int val; 13 * TreeNode *left; 14 * TreeNode *right; 15 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 16 * }; 17 */ 18 class Solution { 19 public: 20 TreeNode *sortedListToBST(ListNode *head) { 21 if(head == NULL) return NULL; 22 ListNode *p, *mid, *pre; 23 for(p = mid = head, pre = NULL; p->next != NULL; mid = mid->next) 24 { 25 p = p->next; 26 if(p->next == NULL) break; 27 p = p->next; 28 pre = mid; 29 }; 30 TreeNode *root = new TreeNode(mid->val); 31 if(pre != NULL) pre->next = NULL, root->left = sortedListToBST(head); 32 else root->left = NULL; 33 root->right = sortedListToBST(mid->next); 34 if(pre != NULL) pre->next = mid; 35 return root; 36 } 37 };
Convert Sorted Array to Binary Search Tree
递归做,比链表的容易些。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 TreeNode *convert(vector<int> &num, int left, int right) 13 { 14 if(right == left) return NULL; 15 int mid = right + left >> 1; 16 TreeNode *root = new TreeNode(num[mid]); 17 root->left = convert(num, left, mid); 18 root->right = convert(num, mid + 1, right); 19 } 20 TreeNode *sortedArrayToBST(vector<int> &num) { 21 return convert(num, 0, num.size()); 22 } 23 };
Binary Tree Level Order Traversal II
宽搜和深搜都可以,找对层数就行了。
本以为这题亮点是如何一遍实现从底向上顺序的vector,AC之后上网一查也全是最后把vector翻转的。。。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<vector<int> >v; 13 void DFS(TreeNode *now, int depth) 14 { 15 if(v.size() <= depth) v.push_back(vector<int>(0)); 16 v[depth].push_back(now->val); 17 if(now->left != NULL) DFS(now->left, depth + 1); 18 if(now->right != NULL) DFS(now->right, depth + 1); 19 } 20 vector<vector<int> > levelOrderBottom(TreeNode *root) { 21 if(root == NULL) return v; 22 DFS(root, 0); 23 for(int i = 0, j = v.size() - 1; i < j; i ++, j --) 24 swap(v[i], v[j]); 25 return v; 26 } 27 };
Construct Binary Tree from Inorder and Postorder Traversal
数据结构经典题。后序遍历的结尾是根节点 Proot,在中序遍历中找到这个节点 Iroot,则 Iroot两边即为左右子树。根据左右子树节点个数,在后序遍历中找到左右子树分界(左右子树肯定不交叉),则几个关键分界点都找到了,对左右子树递归。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 TreeNode *build(vector<int> &inorder, int ileft, int iright, vector<int> &postorder, int pleft, int pright) 13 { 14 if(iright == ileft) 15 return NULL; 16 int root; 17 for(root = ileft; inorder[root] != postorder[pright - 1]; root ++); 18 TreeNode *node = new TreeNode(inorder[root]); 19 node->left = build(inorder, ileft, root, postorder, pleft, pleft + root - ileft); 20 node->right = build(inorder, root + 1, iright, postorder, pleft + root - ileft, pright - 1); 21 return node; 22 } 23 TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) { 24 return build(inorder, 0, inorder.size(), postorder, 0, postorder.size()); 25 } 26 };
Construct Binary Tree from Preorder and Inorder Traversal
和上一题Construct Binary Tree from Inorder and Postorder Traversal方法一样,前序和后序的信息作用相同。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 TreeNode *build(vector<int> &inorder, int ileft, int iright, vector<int> &preorder, int pleft, int pright) 13 { 14 if(iright == ileft) 15 return NULL; 16 int root; 17 for(root = ileft; inorder[root] != preorder[pleft]; root ++); 18 TreeNode *node = new TreeNode(inorder[root]); 19 node->left = build(inorder, ileft, root, preorder, pleft + 1, pleft + root - ileft); 20 node->right = build(inorder, root + 1, iright, preorder, pleft + root - ileft + 1, pright); 21 return node; 22 } 23 TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder) { 24 return build(inorder, 0, inorder.size(), preorder, 0, preorder.size()); 25 26 } 27 };
遍历。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 int maxDepth(TreeNode *root) { 13 if(root == NULL) return 0; 14 if(root->left == NULL) return maxDepth(root->right) + 1; 15 if(root->right == NULL) return maxDepth(root->left) + 1; 16 return max(maxDepth(root->left), maxDepth(root->right)) + 1; 17 } 18 };
Binary Tree Zigzag Level Order Traversal
BFS,奇偶层轮流走,一层左到右,一层右到左。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<vector<int> > ans; 13 vector<vector<int> > zigzagLevelOrder(TreeNode *root) { 14 if(root == NULL) return ans; 15 vector<TreeNode*> q1, q2; 16 q1.push_back(root); 17 int depth = 0; 18 while(!q1.empty() || !q2.empty()) 19 { 20 ans.push_back(vector<int>(0)); 21 for(int i = q1.size() - 1; i >= 0; i --) 22 { 23 ans[depth].push_back(q1[i]->val); 24 if(q1[i]->left != NULL) q2.push_back(q1[i]->left); 25 if(q1[i]->right != NULL) q2.push_back(q1[i]->right); 26 } 27 depth ++; 28 q1.clear(); 29 if(q2.empty()) continue; 30 ans.push_back(vector<int>(0)); 31 for(int i = q2.size() - 1; i >= 0; i --) 32 { 33 ans[depth].push_back(q2[i]->val); 34 if(q2[i]->right != NULL) q1.push_back(q2[i]->right); 35 if(q2[i]->left != NULL) q1.push_back(q2[i]->left); 36 } 37 q2.clear(); 38 depth ++; 39 } 40 return ans; 41 } 42 };
Binary Tree Level Order Traversal
懒省事直接在上一题Binary Tree Zigzag Level Order Traversal的代码上改了一下。
只用一个队列的话,增加个层数信息存队列里即可。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<vector<int> > ans; 13 vector<vector<int> > levelOrder(TreeNode *root) { 14 if(root == NULL) return ans; 15 vector<TreeNode*> q1, q2; 16 q1.push_back(root); 17 int depth = 0; 18 while(!q1.empty() || !q2.empty()) 19 { 20 ans.push_back(vector<int>(0)); 21 for(int i = 0; i < q1.size(); i ++) 22 { 23 ans[depth].push_back(q1[i]->val); 24 if(q1[i]->left != NULL) q2.push_back(q1[i]->left); 25 if(q1[i]->right != NULL) q2.push_back(q1[i]->right); 26 } 27 depth ++; 28 q1.clear(); 29 if(q2.empty()) continue; 30 ans.push_back(vector<int>(0)); 31 for(int i = 0; i < q2.size(); i ++) 32 { 33 ans[depth].push_back(q2[i]->val); 34 if(q2[i]->left != NULL) q1.push_back(q2[i]->left); 35 if(q2[i]->right != NULL) q1.push_back(q2[i]->right); 36 } 37 q2.clear(); 38 depth ++; 39 } 40 return ans; 41 } 42 };
Symmetric Tree
递归:左指针和右指针,对称递归,即“左的左”和“右的右”对应,“左的右”和“右的左”对应。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 bool judge(TreeNode *l, TreeNode *r) 13 { 14 if(l->val != r->val) return false; 15 if(l->left != r->right && (l->left == NULL || r->right == NULL) 16 || l->right != r->left && (l->right == NULL || r->left == NULL)) 17 return false; 18 if(l->left != NULL && !judge(l->left, r->right)) return false; 19 if(l->right != NULL && !judge(l->right, r->left)) return false; 20 return true; 21 } 22 bool isSymmetric(TreeNode *root) { 23 if(root == NULL) return true; 24 if(root->left == NULL && root->right == NULL) return true; 25 else if(root->left != NULL && root->right == NULL 26 || root->left == NULL && root->right != NULL) return false; 27 return judge(root->left, root->right); 28 } 29 };
非递归:左右子树分别做一个队列,同步遍历。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 bool isSymmetric(TreeNode *root) { 13 if(root == NULL) return true; 14 if(root->left == NULL && root->right == NULL) return true; 15 else if(root->left != NULL && root->right == NULL 16 || root->left == NULL && root->right != NULL) return false; 17 queue<TreeNode *> q1, q2; 18 q1.push(root->left); 19 q2.push(root->right); 20 while(!q1.empty()) 21 { 22 TreeNode *now1 = q1.front(), *now2 = q2.front(); 23 q1.pop(); 24 q2.pop(); 25 if(now1->val != now2->val) return false; 26 if(now1->left != NULL || now2->right != NULL) 27 { 28 if(now1->left == NULL || now2->right == NULL) return false; 29 q1.push(now1->left); 30 q2.push(now2->right); 31 } 32 if(now1->right != NULL || now2->left != NULL) 33 { 34 if(now1->right == NULL || now2->left == NULL) return false; 35 q1.push(now1->right); 36 q2.push(now2->left); 37 } 38 } 39 return true; 40 } 41 };
Same Tree
同步遍历,比较判断。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 bool isSameTree(TreeNode *p, TreeNode *q) { 13 if(p == NULL && q == NULL) return true; 14 if(p != q && (p == NULL || q == NULL) || p->val != q->val) return false; 15 return isSameTree(p->left, q->left) && isSameTree(p->right, q->right); 16 } 17 };
中序遍历是二叉查找树的顺序遍历,*a, *b表示前驱节点和当前节点,因为只有一对数值翻转了,所以肯定会遇到前驱节点val比当前节点val大的情况一次或两次,遇到一次表示翻转的是相邻的两个节点。*ans1和*ans2指向两个被翻转的节点,当遇到前驱val比当前val大的情况时候,根据第一次还是第二次给ans1和ans2赋值,最终翻转回来。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 TreeNode *a, *b; 13 TreeNode *ans1, *ans2; 14 bool DFS(TreeNode *now) 15 { 16 if(now->left != NULL) 17 DFS(now->left); 18 a = b; 19 b = now; 20 if(a != NULL && a->val > b->val) 21 { 22 if(ans1 == NULL) ans1 = a; 23 ans2 = b; 24 } 25 if(now->right != NULL) 26 DFS(now->right); 27 } 28 void recoverTree(TreeNode *root) { 29 if(root == NULL) return; 30 a = b = ans1 = ans2 = NULL; 31 DFS(root); 32 swap(ans1->val, ans2->val); 33 } 34 };
Validate Binary Search Tree
中序遍历,更新前驱节点,与当前节点比较。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 TreeNode *pre = NULL; 13 bool isValidBST(TreeNode *root) { 14 if(root == NULL) return true; 15 if(root->left != NULL && !isValidBST(root->left)) return false; 16 if(pre != NULL && pre->val >= root->val) return false; 17 pre = root; 18 if(root->right != NULL && !isValidBST(root->right)) return false; 19 return true; 20 } 21 };
Interleaving String
动态规划。如果结果是true,则任意 i, j,s1 i 之前的字符 和 s2 j 之前的字符,都能够交叉为 s3 i + j 之前的字符。
由此,当dp[i][j]时,如果s1[i]==s3[i+j],则尝试s1[i]与s3[i+j]对应,如果dp[i-1][j]是true,则dp[i][j]也为true。如果s2[j]==s3[i+j]则同样处理。
直到最后,判断dp[s1.length()-1][s2.length()-1]是否为true。为方便初始化,坐标后移了一位。
题目不厚道的出了s1.length()+s2.length() != s3.length()的数据,特判一下。
看到网上也都是O(n^2)的解法,我也就放心了。。。
1 class Solution { 2 public: 3 bool isInterleave(string s1, string s2, string s3) { 4 if(s1.length() + s2.length() != s3.length()) return false; 5 vector<vector<bool> > dp(s1.length() + 1, vector<bool>(s2.length() + 1, false)); 6 for(int i = 0; i <= s1.length(); i ++) 7 for(int j = 0; j <= s2.length(); j ++) 8 { 9 if(!i && !j) dp[i][j] = true; 10 dp[i][j] = dp[i][j] || i > 0 && s3[i + j - 1] == s1[i - 1] && dp[i - 1][j]; 11 dp[i][j] = dp[i][j] || j > 0 && s3[i + j - 1] == s2[j - 1] && dp[i][j - 1]; 12 } 13 return dp[s1.length()][s2.length()]; 14 } 15 };
Unique Binary Search Trees II
LeetCode目前为止感觉最暴力的。递归遍历所有情况,每次返回子问题(左右子树)的vector<TreeNode *>的解,两层循环组合这些解。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<TreeNode *> generate(int start, int end) 13 { 14 vector<TreeNode *>res; 15 if(start > end) 16 { 17 TreeNode *tmp = NULL; 18 res.push_back(tmp); 19 return res; 20 } 21 for(int i = start; i <= end; i ++) 22 { 23 vector<TreeNode *> l = generate(start, i - 1), r = generate(i + 1, end); 24 for(int j = 0; j < l.size(); j ++) 25 for(int k = 0; k < r.size(); k ++) 26 { 27 TreeNode *tmp = new TreeNode(i); 28 tmp->left = l[j]; 29 tmp->right = r[k]; 30 res.push_back(tmp); 31 } 32 } 33 return res; 34 } 35 vector<TreeNode *> generateTrees(int n) { 36 return generate(1, n); 37 } 38 };
Unique Binary Search Trees
经典问题,卡特兰数,可递推,可用公式(公式用组合数,也要写循环)。
1 class Solution { 2 public: 3 int COM(int n, int m) 4 { 5 m = n - m < m ? n - m : m; 6 int res, i, j; 7 for(i = n, res = j = 1; i > n - m; i --) 8 { 9 res *= i; 10 for(; j <= m && res % j == 0; j ++) 11 res /= j; 12 } 13 return res; 14 } 15 int numTrees(int n) { 16 return COM(n << 1, n) / (n + 1); 17 18 } 19 };
Binary Tree Inorder Traversal
数据结构基础
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<int> res; 13 void inorder(TreeNode *now) 14 { 15 if(now == NULL) return; 16 inorder(now->left); 17 res.push_back(now->val); 18 inorder(now->right); 19 } 20 vector<int> inorderTraversal(TreeNode *root) { 21 inorder(root); 22 return res; 23 } 24 };
Restore IP Addresses
四层递归枚举分割位置,判断数字范围和前导零,处理字符串。
1 class Solution { 2 public: 3 vector<string> res; 4 void DFS(string s, int last, int cur, string now) 5 { 6 if(cur == 3) 7 { 8 if(last == s.length()) return; 9 string tmp = s.substr(last, s.length() - last); 10 if(atoi(tmp.c_str()) <= 255 && (tmp.length() == 1 || tmp[0] != '0')) 11 res.push_back(now + tmp); 12 return; 13 } 14 string lin; 15 for(int i = last; i < s.length(); i ++) 16 { 17 string tmp = s.substr(last, i - last + 1); 18 if(atoi(tmp.c_str()) <= 255 && (tmp.length() == 1 || tmp[0] != '0')) 19 DFS(s, i + 1, cur + 1, now + tmp + "."); 20 } 21 } 22 vector<string> restoreIpAddresses(string s) { 23 DFS(s, 0, 0, ""); 24 return res; 25 } 26 };
Reverse Linked List II
在表头添加一个“哨兵”会好写很多,额外的newhead可以帮助标记翻转之后更换了的头指针。
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 *reverseBetween(ListNode *head, int m, int n) { 12 ListNode *newhead = new ListNode(0); 13 newhead->next = head; 14 ListNode *pre = newhead, *p = head, *start = NULL; 15 ListNode *tmp; 16 for(int i = 1; p != NULL; i ++) 17 { 18 tmp = p->next; 19 if(i == m) 20 start = pre; 21 if(i > m && i <= n) 22 p->next = pre; 23 if(i == n) 24 { 25 start->next->next = tmp; 26 start->next = p; 27 } 28 pre = p; 29 p = tmp; 30 } 31 tmp = newhead->next; 32 free(newhead); 33 return tmp; 34 } 35 };
Subsets II
统计地存map里,map[i]= j 表示 S 中有 j 个 i。map是有序的,用迭代器递归枚举放入集合的个数。
也可以先排序,用set标记每个数时候被放入过,第一次放入之后才可以继续放同一个数。
代码是用map的方法。
1 class Solution { 2 public: 3 vector<vector<int> > res; 4 vector<int> now; 5 map<int, int> mp; 6 void DFS(map<int, int>::iterator i) 7 { 8 if(i == mp.end()) 9 { 10 res.push_back(now); 11 return; 12 } 13 map<int, int>::iterator tmp = i; 14 i ++; 15 DFS(i); 16 for(int j = 0; j < tmp->second; j ++) 17 { 18 now.push_back(tmp->first); 19 DFS(i); 20 } 21 for(int j = 0; j < tmp->second; j ++) 22 now.pop_back(); 23 } 24 vector<vector<int> > subsetsWithDup(vector<int> &S) { 25 for(int i = 0; i < S.size(); i ++) 26 !mp.count(S[i]) ? (mp[S[i]] = 1) : mp[S[i]] ++; 27 DFS(mp.begin()); 28 return res; 29 } 30 };
Decode Ways
递推:dp[i]表示前 i 个数字的解码种数。
dp[i] = if(一)dp[i-1] + if(二)dp[i-2]
当 i 位置不为0,可加上 i - 1 位置的解。当当前位置和前一位置组成的两位数满足解码且高位不为0,可加上 i - 2 位置的解。
1 class Solution { 2 public: 3 int numDecodings(string s) { 4 if(s.length() == 0) return 0; 5 vector<int> dp(s.length() + 1, 0); 6 dp[0] = 1; 7 for(int i = 0; i < s.length(); i ++) 8 { 9 dp[i + 1] = (s[i] != '0' ? dp[i] : 0) + 10 (i > 0 && s[i - 1] != '0' && atoi(s.substr(i - 1, 2).c_str()) <= 26 ? dp[i - 1] : 0); 11 } 12 return dp[s.length()]; 13 } 14 };
Gray Code
格雷码有多种生成方法,可参考维基百科。
1 class Solution { 2 public: 3 vector<int> grayCode(int n) { 4 vector<int> res; 5 for(int i = 0; i < (1 << n); i ++) 6 res.push_back((i >> 1) ^ i); 7 return res; 8 } 9 };
Merge Sorted Array
从后往前,对 A 来说一个萝卜一个坑,肯定不会破坏前面的数据。具体看代码。
1 class Solution { 2 public: 3 void merge(int A[], int m, int B[], int n) { 4 int p = m + n - 1, i = m - 1, j = n - 1; 5 for(; i >= 0 && j >= 0;) 6 { 7 if(A[i] > B[j]) A[p --] = A[i --]; 8 else A[p --] = B[j --]; 9 } 10 while(i >= 0) A[p --] = A[i --]; 11 while(j >= 0) A[p --] = B[j --]; 12 } 13 };
Scramble String
直接搜索可以过,记忆化搜索可提高效率。
dp[i][j][k]表示从 s1[i] 和 s2[j] 开始长度为 k 的字符串是否是scrambled string。
枚举分割位置,scrambled string要求字符串对应字母的个数是一致的,可以直接排序对比。递归终点是刚好只有一个字母。
1 class Solution { 2 public: 3 string S1, S2; 4 vector<vector<vector<int> > > dp; 5 bool judge(string a, string b) 6 { 7 sort(a.begin(), a.end()); 8 sort(b.begin(), b.end()); 9 for(int i = 0; i < a.length(); i ++) 10 if(a[i] != b[i]) return false; 11 return true; 12 } 13 int DFS(int s1start, int s2start, int len) 14 { 15 int &ans = dp[s1start][s2start][len - 1]; 16 if(len == 1) return ans = S1[s1start] == S2[s2start]; 17 if(ans != -1) return ans; 18 if(!judge(S1.substr(s1start, len), S2.substr(s2start, len))) return ans = 0; 19 ans = 0; 20 for(int i = 1; i < len; i ++) 21 { 22 ans = ans 23 || DFS(s1start, s2start, i) && DFS(s1start + i, s2start + i, len - i) 24 || DFS(s1start, s2start + len - i, i) && DFS(s1start + i, s2start, len - i); 25 26 } 27 return ans; 28 } 29 bool isScramble(string s1, string s2) { 30 S1 = s1, S2 = s2; 31 dp = vector<vector<vector<int> > > 32 (s1.length(), vector<vector<int> > 33 (s1.length(), vector<int> 34 (s1.length(), -1))); 35 return DFS(0, 0, s1.length()); 36 } 37 };
Partition List
分存大小最后合并。
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 *partition(ListNode *head, int x) { 12 ListNode *shead, *bhead, *smaller, *bigger, *p; 13 for(shead = bhead = smaller = bigger = NULL, p = head; p != NULL; p = p->next) 14 { 15 if(p->val < x) 16 { 17 if(shead == NULL) 18 shead = p; 19 if(smaller != NULL) 20 smaller->next = p; 21 smaller = p; 22 } 23 else 24 { 25 if(bhead == NULL) 26 bhead = p; 27 if(bigger != NULL) 28 bigger->next = p; 29 bigger = p; 30 } 31 } 32 if(smaller != NULL) smaller->next = bhead; 33 if(bigger != NULL) bigger->next = NULL; 34 return shead != NULL ? shead : bhead; 35 } 36 };
Maximal Rectangle
方法一:linecnt[i][j]统计第 i 行到第 j 位置有多少个连续的 '1',接下来枚举列,每一列相当于一次直方图最大矩形统计,计算每个位置向前和向后最远的不少于当前位置值的位置,每次更新结果,总复杂度O(n^2)。
找“最远位置”用迭代指针,理论复杂度略高于O(n)。
1 class Solution { 2 public: 3 int maximalRectangle(vector<vector<char> > &matrix) { 4 if(matrix.size() == 0) return 0; 5 int H = matrix.size(), W = matrix[0].size(); 6 int ans = 0; 7 vector<int> left(H), right(H); 8 vector<vector<int> > linecnt(H, vector<int>(W, 0)); 9 for(int i = 0; i < H; i ++) 10 { 11 int last = 0; 12 for(int j = 0; j < W; j ++) 13 { 14 if(matrix[i][j] == '1') last ++; 15 else last = 0; 16 linecnt[i][j] = last; 17 } 18 } 19 for(int k = 0; k < W; k ++) 20 { 21 for(int i = 0; i < H; i ++) 22 { 23 if(i == 0) left[i] = -1; 24 else 25 { 26 left[i] = i - 1; 27 while(left[i] > -1 && linecnt[left[i]][k] >= linecnt[i][k]) 28 left[i] = left[left[i]]; 29 } 30 } 31 for(int i = H - 1; i >= 0; i --) 32 { 33 if(i == H - 1) right[i] = H; 34 else 35 { 36 right[i] = i + 1; 37 while(right[i] < H && linecnt[right[i]][k] >= linecnt[i][k]) 38 right[i] = right[right[i]]; 39 } 40 ans = max(ans, (right[i] - left[i] - 1) * linecnt[i][k]); 41 } 42 } 43 return ans; 44 } 45 };
用单调栈,理论复杂度O(n)。
1 class Solution { 2 public: 3 int maximalRectangle(vector<vector<char> > &matrix) { 4 if(matrix.size() == 0) return 0; 5 vector<vector<int> > linecnt(matrix.size(), vector<int>(matrix[0].size(), 0)); 6 for(int i = 0; i < matrix.size(); i ++) 7 { 8 int last = 0; 9 for(int j = 0; j < matrix[0].size(); j ++) 10 { 11 if(matrix[i][j] == '1') last ++; 12 else last = 0; 13 linecnt[i][j] = last; 14 } 15 } 16 int ans = 0; 17 for(int k = 0; k < matrix[0].size(); k ++) 18 { 19 stack<int> s, site; 20 vector<int>last(matrix.size()); 21 for(int i = 0; i < matrix.size(); i ++) 22 { 23 while(!s.empty() && s.top() >= linecnt[i][k]) 24 s.pop(), site.pop(); 25 if(!s.empty()) last[i] = site.top() + 1; 26 else last[i] = 0; 27 s.push(linecnt[i][k]); 28 site.push(i); 29 } 30 while(!s.empty()) s.pop(), site.pop(); 31 for(int i = matrix.size() - 1; i >= 0; i --) 32 { 33 while(!s.empty() && s.top() >= linecnt[i][k]) 34 s.pop(), site.pop(); 35 if(!s.empty()) ans = max(ans, (site.top() - last[i]) * linecnt[i][k]); 36 else ans = max(ans, (int)(matrix.size() - last[i]) * linecnt[i][k]); 37 s.push(linecnt[i][k]); 38 site.push(i); 39 } 40 } 41 return ans; 42 } 43 };
方法二:每个 '1' 的点当作一个矩形的底部,left[j]、right[j]、height[j]表示当前行第 j 个位置这个点向左、右、上伸展的最大矩形的边界,作为滚动数组,下一行的数据可以由上一行结果得到,总复杂度O(n^2)。
left[j] = max(这一行最左, left[j](上一行最左) );
right[j] = min(这一行最右,right[j](上一行最右) );
height[j] = height[j - 1] + 1;
1 class Solution { 2 public: 3 int maximalRectangle(vector<vector<char> > &matrix) { 4 if(matrix.size() == 0) return 0; 5 int H = matrix.size(), W = matrix[0].size(); 6 vector<int> left(W, -1), right(W, W), height(W, 0); 7 int ans = 0; 8 for(int i = 0; i < H; i ++) 9 { 10 int last = -1; 11 for(int j = 0; j < W; j ++) 12 { 13 if(matrix[i][j] == '1') 14 { 15 if(last == -1) last = j; 16 left[j] = max(left[j], last); 17 height[j] ++; 18 } 19 else 20 { 21 last = -1; 22 left[j] = -1; 23 height[j] = 0; 24 } 25 } 26 last = -1; 27 for(int j = W - 1; j >= 0; j --) 28 { 29 if(matrix[i][j] == '1') 30 { 31 if(last == -1) last = j; 32 right[j] = min(right[j], last); 33 ans = max(ans, height[j] * (right[j] - left[j] + 1)); 34 } 35 else 36 { 37 last = -1; 38 right[j] = W; 39 } 40 } 41 } 42 return ans; 43 } 44 };
Largest Rectangle in Histogram
参考上一题Maximal Rectangle方法一。
1 class Solution { 2 public: 3 int largestRectangleArea(vector<int> &height) { 4 if(height.size() == 0) return 0; 5 vector<int> left(height.size()), right(height.size()); 6 int ans = 0; 7 for(int i = 0; i < height.size(); i ++) 8 { 9 if(i == 0) left[i] = -1; 10 else 11 { 12 left[i] = i - 1; 13 while(left[i] > -1 && height[i] <= height[left[i]]) 14 left[i] = left[left[i]]; 15 } 16 } 17 for(int i = height.size() - 1; i >= 0; i --) 18 { 19 if(i == height.size() - 1) right[i] = height.size(); 20 else 21 { 22 right[i] = i + 1; 23 while(right[i] < height.size() && height[i] <= height[right[i]]) 24 right[i] = right[right[i]]; 25 } 26 ans = max(ans, (right[i] - left[i] - 1) * height[i]); 27 } 28 return ans; 29 } 30 };
Remove Duplicates from Sorted List II
加个表头乱搞吧。
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 *deleteDuplicates(ListNode *head) { 12 if(head == NULL || head->next == NULL) return head; 13 ListNode *newhead = new ListNode(0); 14 newhead->next = head; 15 for(ListNode *pre = newhead, *now = head, *nex = head->next; nex != NULL;) 16 { 17 if(now->val == nex->val) 18 { 19 while(nex != NULL && now->val == nex->val) 20 { 21 free(now); 22 now = nex; 23 nex = nex->next; 24 } 25 free(now); 26 pre->next = nex; 27 if(nex == NULL) break; 28 } 29 else pre = now; 30 now = nex; 31 nex = nex->next; 32 } 33 head = newhead->next; 34 free(newhead); 35 return head; 36 } 37 };
Remove Duplicates from Sorted List
直接操作。
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 *deleteDuplicates(ListNode *head) { 12 if(head == NULL || head->next == NULL) return head; 13 for(ListNode *pre = head, *p = head->next; p != NULL;) 14 { 15 while(p != NULL && pre->val == p->val) 16 { 17 pre->next = p->next; 18 free(p); 19 p = pre->next; 20 } 21 if(p == NULL) break; 22 pre = p; 23 p = p->next; 24 } 25 return head; 26 } 27 };
Search in Rotated Sorted Array II
以mid为界,左右两边至少有一边是有序的。由于不可避免地会有O(n)的可能性,所以确定的时候二分,不确定的时候单位缩减边界。
1 class Solution { 2 public: 3 bool search(int A[], int n, int target) { 4 int left = 0, right = n - 1, mid; 5 while(left < right) 6 { 7 mid = left + right >> 1; 8 if(target < A[mid] && A[left] < target) right = mid; 9 else if(target < A[right] && A[mid] < target) left = mid + 1; 10 else 11 { 12 if(A[left] == target || A[right] == target) return true; 13 else if(A[left] < target) left ++; 14 else if(target < A[right]) right --; 15 else return false; 16 } 17 } 18 return A[left] == target ? true : false; 19 } 20 };
Remove Duplicates from Sorted Array II
记下放了几个,多了不放。
1 class Solution { 2 public: 3 int removeDuplicates(int A[], int n) { 4 int i, j, cnt; 5 for(i = j = cnt = 0; i < n; i ++) 6 { 7 if(j != 0 && A[j - 1] == A[i]) cnt ++; 8 else cnt = 0; 9 if(cnt < 2) A[j ++] = A[i]; 10 } 11 return j; 12 } 13 };
基础DFS。
1 class Solution { 2 public: 3 int dx[4] = {1, -1, 0, 0}; 4 int dy[4] = {0, 0, 1, -1}; 5 bool DFS(int x, int y, vector<vector<char> > &board, string word, int ith) 6 { 7 if(board[x][y] != word[ith]) return false; 8 if(ith == word.length() - 1) return true; 9 board[x][y] = '.'; 10 for(int i = 0; i < 4; i ++) 11 { 12 int nx = x + dx[i]; 13 int ny = y + dy[i]; 14 if(nx >= 0 && ny >= 0 && nx < board.size() && ny < board[0].size()) 15 { 16 if(DFS(nx, ny, board, word, ith + 1)) 17 { 18 board[x][y] = word[ith]; 19 return true; 20 } 21 } 22 } 23 board[x][y] = word[ith]; 24 return false; 25 } 26 bool exist(vector<vector<char> > &board, string word) { 27 for(int i = 0; i < board.size(); i ++) 28 { 29 for(int j = 0; j < board[0].size(); j ++) 30 { 31 if(DFS(i, j, board, word, 0)) return true; 32 } 33 } 34 return false; 35 } 36 };
Subsets
基础DFS。
1 class Solution { 2 public: 3 vector<int> now; 4 vector<vector<int> > res; 5 void DFS(vector<int> &S, int ith) 6 { 7 if(ith == S.size()) 8 { 9 res.push_back(now); 10 return; 11 } 12 DFS(S, ith + 1); 13 now.push_back(S[ith]); 14 DFS(S, ith + 1); 15 now.pop_back(); 16 } 17 vector<vector<int> > subsets(vector<int> &S) { 18 sort(S.begin(), S.end()); 19 DFS(S, 0); 20 return res; 21 } 22 };
Combinations
基础DFS。
1 class Solution { 2 public: 3 vector<int> now; 4 vector<vector<int> > res; 5 void DFS(int n, int ith, int sum, int k) 6 { 7 if(sum == k) 8 { 9 res.push_back(now); 10 return; 11 } 12 if(sum + n - ith + 1 > k) 13 { 14 DFS(n, ith + 1, sum, k); 15 } 16 now.push_back(ith); 17 DFS(n, ith + 1, sum + 1, k); 18 now.pop_back(); 19 } 20 vector<vector<int> > combine(int n, int k) { 21 DFS(n, 1, 0, k); 22 return res; 23 } 24 };
Minimum Window Substring
先统计 T 中各字符都有多少个,然后两个下标一前(i)一后(j)在 S 上跑, 当 i 跑到把 T 中字符都包含的位置时候,让 j 追到第一个包含 T 的字符的地方,更新结果,去掉 j 这个位置字符的统计,让 i 继续跑,如此反复。
i 和 j 都只遍历一遍 S,复杂度 O(n)。
1 class Solution { 2 public: 3 string minWindow(string S, string T) { 4 vector<int> cnt(256, 0), need(256, 0); 5 int sum = 0, len = 0x3f3f3f3f; 6 string ans; 7 for(int i = 0; i < T.size(); i ++) 8 need[T[i]] ++; 9 for(int i = 0, j = 0; i < S.length(); j ++) 10 { 11 while(i < S.length() && sum < T.length()) 12 { 13 if(cnt[S[i]] < need[S[i]]) 14 sum ++; 15 cnt[S[i]] ++; 16 i ++; 17 } 18 while(sum == T.length() && j < S.length()) 19 { 20 cnt[S[j]] --; 21 if(cnt[S[j]] < need[S[j]]) 22 break; 23 j ++; 24 } 25 if(sum < T.length()) break; 26 if(i - j < len) 27 ans = S.substr(j, i - j), len = i - j; 28 sum --; 29 } 30 return ans; 31 } 32 };
轮流找:
1 class Solution { 2 public: 3 void sortColors(int A[], int n) { 4 int find = 0; 5 for(int i = 0, j = n - 1; i < n; i ++) 6 { 7 if(A[i] == find) continue; 8 while(j > i && A[j] != find) j --; 9 if(j > i) swap(A[i], A[j]); 10 else i --, j = n - 1, find ++; 11 } 12 } 13 };
找到哪个放哪个:
1 class Solution { 2 public: 3 void sortColors(int A[], int n) { 4 int p0, p1, p2; 5 for(p0 = 0, p1 = p2 = n - 1; p0 < p1; ) 6 { 7 if(A[p0] == 0) p0 ++; 8 if(A[p0] == 1) swap(A[p0], A[p1 --]); 9 if(A[p0] == 2) 10 { 11 swap(A[p0], A[p2 --]); 12 p1 = p2; 13 } 14 } 15 } 16 };
Search a 2D Matrix
写两个二分查找。或者把整个矩阵看作一维,直接二分,换算坐标。
1 class Solution { 2 public: 3 bool searchMatrix(vector<vector<int> > &matrix, int target) { 4 int left, right, mid; 5 for(left = 0, right = matrix.size(); left < right - 1; ) 6 { 7 mid = left + right >> 1; 8 if(matrix[mid][0] > target) right = mid; 9 else left = mid; 10 } 11 if(left == matrix.size() || right == 0) return false; 12 vector<int> &a = matrix[left]; 13 for(left = 0, right = a.size(); left < right - 1;) 14 { 15 mid = left + right >> 1; 16 if(a[mid] > target) right = mid; 17 else left = mid; 18 } 19 if(left == a.size() || right == 0) return false; 20 return a[left] == target; 21 } 22 };
Set Matrix Zeroes
O(m+n)的方法是容易想到的,而空间复杂度O(1),只要利用原矩阵的一行和一列来使用O(m+n)的方法就行了。
1 class Solution { 2 public: 3 void setZeroes(vector<vector<int> > &matrix) { 4 if(matrix.size() == 0) return; 5 int x = -1, y = -1; 6 for(int i = 0; i < matrix.size(); i ++) 7 { 8 for(int j = 0; j < matrix[0].size(); j ++) 9 { 10 if(matrix[i][j] == 0) 11 { 12 if(x == -1) 13 { 14 x = i, y = j; 15 } 16 else 17 { 18 matrix[x][j] = 0; 19 matrix[i][y] = 0; 20 } 21 } 22 } 23 } 24 if(x == -1) return; 25 for(int i = 0; i < matrix.size(); i ++) 26 for(int j = 0; j < matrix[0].size(); j ++) 27 if((matrix[x][j] == 0 || matrix[i][y] == 0) && (i != x && j != y)) matrix[i][j] = 0; 28 for(int i = 0; i < matrix.size(); i ++) matrix[i][y] = 0; 29 for(int j = 0; j < matrix[0].size(); j ++) matrix[x][j] = 0; 30 } 31 };
Edit Distance
动态规划,先初始化 dp[i][0] 和 dp[0][i],即每个字符串对应空串的编辑距离为串长度,之后对每个位置取子问题加上当前位置 改、删、增得解的最小值。
1 class Solution { 2 public: 3 int minDistance(string word1, string word2) { 4 vector<vector<int> > dp(word1.length() + 1, vector<int>(word2.length() + 1, 0)); 5 for(int i = 0; i < word1.length(); i ++) dp[i + 1][0] = i + 1; 6 for(int i = 0; i < word2.length(); i ++) dp[0][i + 1] = i + 1; 7 for(int i = 0; i < word1.length(); i ++) 8 for(int j = 0; j < word2.length(); j ++) 9 { 10 if(word1[i] != word2[j]) 11 dp[i + 1][j + 1] = min(dp[i][j] + 1, min(dp[i][j + 1] + 1, dp[i + 1][j] + 1)); 12 else 13 dp[i + 1][j + 1] = min(dp[i][j], min(dp[i][j + 1] + 1, dp[i + 1][j] + 1)); 14 } 15 return dp[word1.length()][word2.length()]; 16 } 17 };
Simplify Path
好烦人的题,没什么好说的。
1 class Solution { 2 public: 3 string simplifyPath(string path) { 4 stack<string> s; 5 string str; 6 for(int i = 0; i < path.length(); i ++) 7 { 8 if(path[i] == '/') 9 { 10 if(str == "..") 11 { 12 if(!s.empty()) 13 s.pop(); 14 } 15 else if(str != "." && str != "") 16 s.push(str); 17 str.clear(); 18 } 19 else 20 str += path[i]; 21 } 22 if(str == "..") 23 { 24 if(!s.empty()) 25 s.pop(); 26 } 27 else if(str != "." && str != "") 28 s.push(str); 29 if(s.empty()) return "/"; 30 for(str.clear(); !s.empty(); s.pop()) 31 str = "/" + s.top() + str; 32 return str; 33 } 34 };
Climbing Stairs
递推,就是斐波那契数列。
1 class Solution { 2 public: 3 int climbStairs(int n) { 4 return (int) 5 (pow((1+sqrt(5))/2, n + 1) / sqrt(5) - 6 pow((1-sqrt(5))/2, n + 1) / sqrt(5) + 0.1); 7 } 8 };
Sqrt(x)
牛顿迭代。
设输入为n,f(x)=x^2-n,解就是f(x)=0时的x。
设猜了一数x[0],那么在f(x)在x[0]处的切线与x轴的交点x[1]更接近目标解(可画图看看)。
那么递推下去,x[i]=(x[i-1]+n/x[i-1])/2,用double,越推越精确,直到自己想要的精度。
1 class Solution { 2 public: 3 int sqrt(int x) { 4 double now, last; 5 if(x == 0) return 0; 6 for(now = last = (double)x; ; last = now) 7 { 8 now = (last + (x / last)) * 0.5; 9 if(fabs(last - now) < 1e-5) break; 10 } 11 return (int)(now + 1e-6); 12 } 13 };
Text Justification
每行限制长度,空格均匀插入,不能完全平均的情况下优先靠前的单词间隔。
最后一行特别处理,单词间只有一个空格,剩下的放在末尾。
1 class Solution { 2 public: 3 vector<string> fullJustify(vector<string> &words, int L) { 4 vector<string> ans; 5 int cnt = 0, i, j, k, l; 6 for(i = 0, j = 0; j < words.size(); i ++) 7 { 8 if(i < words.size()) 9 { 10 cnt += words[i].length(); 11 if(i == j) continue; 12 } 13 if(i == words.size() || (L - cnt) / (i - j) < 1) 14 { 15 int blank = 0; 16 if(i < words.size()) blank = (i - j - 1) ? (L - cnt + words[i].length()) / (i - j - 1) : 0; 17 string tmp = ""; 18 l = i < words.size() ? (L - cnt + words[i].length() - blank * (i - j - 1)) : 0; 19 for(k = j; k < i; k ++, l --) 20 { 21 tmp += words[k]; 22 if(k != i - 1) 23 { 24 if(i != words.size()) 25 { 26 for(int bl = 0; bl < blank; bl ++) tmp += " "; 27 if(l > 0) tmp += " "; 28 } 29 else 30 tmp += " "; 31 } 32 } 33 while(tmp.length() < L) tmp += " "; 34 ans.push_back(tmp); 35 cnt = 0; 36 j = i; 37 i --; 38 } 39 } 40 return ans; 41 } 42 };
Plus One
大整数加法。
1 class Solution { 2 public: 3 vector<int> plusOne(vector<int> &digits) { 4 int cur, i; 5 if(digits.size() == 0) return digits; 6 for(i = digits.size() - 1, cur = 1; i >= 0; i --) 7 { 8 int tmp = digits[i] + cur; 9 cur = tmp / 10; 10 digits[i] = tmp % 10; 11 } 12 if(cur) digits.insert(digits.begin(), cur); 13 return digits; 14 } 15 };
Valid Number
用DFA也不麻烦,题目定义太模糊,为了理解规则错很多次也没办法。
1 class Solution { 2 public: 3 4 int f[11][129]; 5 const int fail = -1; //非法 6 const int st = 0; //起始 7 const int pn = 1; //正负号 8 const int di = 2; //整数部分 9 const int del = 3; //前面无数字小数点 10 const int ddi = 4; //小数部分 11 const int ndel = 5; //前面有数字小数点 12 const int dibl = 6; //数后空格 13 const int ex = 7; //进入指数 14 const int epn = 8; //指数符号 15 const int edi = 9; //指数数字 16 const int end = 10; //正确结束 17 void buildDFA() 18 { 19 memset(f, -1, sizeof(f)); 20 f[st][' '] = st; 21 f[st]['+'] = f[st]['-'] = pn; 22 for(int i = '0'; i <= '9'; i ++) 23 { 24 f[st][i] = f[pn][i] = f[di][i] = di; 25 f[del][i] = f[ndel][i] = f[ddi][i] = ddi; 26 f[ex][i] = f[epn][i] = f[edi][i] = edi; 27 } 28 f[di]['.'] = ndel; 29 f[st]['.'] = f[pn]['.'] = del; 30 f[di][' '] = f[ndel][' '] = f[ddi][' '] = f[dibl][' '] = f[edi][' '] = dibl; 31 f[di][0] = f[ndel][0] = f[dibl][0] = f[ddi][0] = f[edi][0] = end; 32 f[di]['e'] = f[ndel]['e'] = f[ddi]['e'] = ex; 33 f[ex][' '] = ex; 34 f[ex]['+'] = f[ex]['-'] = epn; 35 } 36 bool DFA(const char *s) 37 { 38 int situ = st; 39 for(int i = 0;; i ++) 40 { 41 situ = f[situ][s[i]]; 42 if(situ == end) return true; 43 if(situ == fail) return false; 44 } 45 return true; 46 } 47 bool isNumber(const char *s) { 48 buildDFA(); 49 return DFA(s); 50 } 51 };
翻转,大整数加法,再翻转。无心情优化。
1 class Solution { 2 public: 3 string addBinary(string a, string b) { 4 reverse(a.begin(), a.end()); 5 reverse(b.begin(), b.end()); 6 string c; 7 int cur = 0, i; 8 for(i = 0; i < min(a.length(), b.length()); i ++) 9 { 10 int tmp = a[i] - '0' + b[i] - '0' + cur; 11 cur = tmp >> 1; 12 c += (tmp & 1) + '0'; 13 } 14 string &t = a.length() > b.length() ? a : b; 15 for(; i < t.length(); i ++) 16 { 17 int tmp = t[i] - '0' + cur; 18 cur = tmp >> 1; 19 c += (tmp & 1) + '0'; 20 } 21 if(cur) c += '1'; 22 reverse(c.begin(), c.end()); 23 return c; 24 } 25 };
归并排序的一次操作,设个哨兵头结点,结束后free。
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 *mergeTwoLists(ListNode *l1, ListNode *l2) { 12 ListNode *thead = new ListNode(0), *p = thead; 13 while(l1 != NULL && l2 != NULL) 14 { 15 if(l1->val < l2->val) p->next = l1, p = l1, l1 = l1->next; 16 else p->next = l2, p = l2, l2 = l2->next; 17 } 18 while(l1 != NULL) p->next = l1, p = l1, l1 = l1->next; 19 while(l2 != NULL) p->next = l2, p = l2, l2 = l2->next; 20 p = thead->next; 21 free(thead); 22 return p; 23 } 24 };
递推
1 class Solution { 2 public: 3 int minPathSum(vector<vector<int> > &grid) { 4 if(grid.size() == 0) return 0; 5 for(int i = 0; i < grid.size(); i ++) 6 { 7 for(int j = 0; j < grid[0].size(); j ++) 8 { 9 int tmp = 0x3f3f3f3f; 10 if(i > 0) tmp = min(tmp, grid[i][j] + grid[i - 1][j]); 11 if(j > 0) tmp = min(tmp, grid[i][j] + grid[i][j - 1]); 12 grid[i][j] = tmp == 0x3f3f3f3f ? grid[i][j] : tmp; 13 } 14 } 15 return grid[grid.size() - 1][grid[0].size() - 1]; 16 } 17 };
Unique Paths II
递推
1 class Solution { 2 public: 3 int uniquePathsWithObstacles(vector<vector<int> > &obstacleGrid) { 4 if(obstacleGrid.size() == 0) return 0; 5 obstacleGrid[0][0] = obstacleGrid[0][0] != 1; 6 for(int i = 0; i < obstacleGrid.size(); i ++) 7 for(int j = 0; j < obstacleGrid[0].size(); j ++) 8 { 9 if(i == 0 && j == 0) continue; 10 if(obstacleGrid[i][j] == 1) 11 { 12 obstacleGrid[i][j] = 0; 13 continue; 14 } 15 if(i > 0) obstacleGrid[i][j] += obstacleGrid[i - 1][j]; 16 if(j > 0) obstacleGrid[i][j] += obstacleGrid[i][j - 1]; 17 } 18 return obstacleGrid[obstacleGrid.size() - 1][obstacleGrid[0].size() - 1]; 19 } 20 };
这是当年学组合数时候的经典题型吧。
1 class Solution { 2 public: 3 int COM(int a, int b) 4 { 5 b = min(b, a - b); 6 int ret = 1, i, j; 7 for(i = a, j = 1; i > a - b; i --) 8 { 9 ret *= i; 10 for(; j <= b && ret % j == 0; j ++) 11 ret /= j; 12 } 13 return ret; 14 } 15 int uniquePaths(int m, int n) { 16 return COM(m + n - 2, m - 1); 17 } 18 };
Rotate List
因为k可能比长度大,需要求长度然后k对长度取模。那么就不要矫情地追求双指针一遍扫描了。
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 *rotateRight(ListNode *head, int k) { 12 if(head == NULL) return NULL; 13 int cnt; 14 ListNode *en, *p; 15 for(cnt = 1, en = head; en->next != NULL; cnt ++, en = en->next); 16 k %= cnt; 17 for(p = head, cnt --; cnt != k; cnt --, p = p->next); 18 en->next = head; 19 en = p->next; 20 p->next = NULL; 21 return en; 22 } 23 };
Permutation Sequence
一位一位算,每一位优先没使用过的较小的数字,而其后剩下的m个位置有 m! 种排列方法,用 k 减去,直到k不大于这个方法数,则这一位就是枚举到的这个数。
1 class Solution { 2 public: 3 int permu[10]; 4 bool vis[10]; 5 string getPermutation(int n, int k) { 6 permu[0] = 1; 7 for(int i = 1; i < 10; i ++) permu[i] = permu[i - 1] * i; 8 memset(vis, 0, sizeof(vis)); 9 string ans; 10 for(int i = 1; i <= n; i ++) 11 { 12 for(int j = 1; j <= n; j ++) 13 { 14 if(!vis[j]) 15 { 16 if(k > permu[n - i]) k -= permu[n - i]; 17 else {ans += '0' + j; vis[j] = true; break;} 18 } 19 } 20 } 21 return ans; 22 } 23 };
Spiral Matrix II
直接算每个位置的数是多少有木有很霸气
先看当前位置之外有几个嵌套的正方形,再看当前位置在当前正方形四条边的第几条,求出坐标(x,y)位置的数。
1 class Solution { 2 public: 3 vector<vector<int> > res; 4 vector<int> nsq; 5 int calnum(int i, int j, int n) 6 { 7 int num, tmp; 8 tmp = min(min(i, j), min(n - 1 - i, n - 1 - j)); 9 num = nsq[tmp]; 10 if(i == tmp) return num + j - tmp + 1; 11 if(n - j - 1 == tmp) return num + n - 2 * tmp + i - tmp; 12 if(n - i - 1 == tmp) return num + 2 * (n - 2 * tmp) - 2 + n - j - tmp; 13 return num + 3 * (n - 2 * tmp) - 3 + n - i - tmp; 14 } 15 vector<vector<int> > generateMatrix(int n) { 16 nsq.push_back(0); 17 for(int i = n; i > 0; i -= 2) nsq.push_back(4 * i - 4); 18 for(int i = 1; i < nsq.size(); i ++) nsq[i] += nsq[i - 1]; 19 for(int i = 0; i < n; i ++) 20 { 21 vector<int> tmp; 22 for(int j = 0; j < n; j ++) 23 { 24 tmp.push_back(calnum(i, j, n)); 25 } 26 res.push_back(tmp); 27 } 28 return res; 29 } 30 };
从后往前找。
1 class Solution { 2 public: 3 int lengthOfLastWord(const char *s) { 4 int i, j; 5 for(i = strlen(s) - 1; i >= 0 && s[i] == ' '; i --); 6 for(j = i - 1; j >= 0 && s[j] != ' '; j --); 7 return i < 0 ? 0 : i - j; 8 } 9 };
Insert Interval
end 比 newInterval 的 start 小的 intervals 直接插入,从 end 比 newInterval 的 start 大的 intervals 开始,到 start 比 newInterval 的 end 大的 intervals 结束,对这部分区间合并,再把之后的 intervals直接插入,特判 newInterval 最小和最大两种极端情况。
1 /** 2 * Definition for an interval. 3 * struct Interval { 4 * int start; 5 * int end; 6 * Interval() : start(0), end(0) {} 7 * Interval(int s, int e) : start(s), end(e) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<Interval> res; 13 vector<Interval> insert(vector<Interval> &intervals, Interval newInterval) { 14 if(intervals.size() == 0) {res.push_back(newInterval); return res;} 15 int i, j; 16 for(i = 0; i < intervals.size() && newInterval.start > intervals[i].end; i ++) 17 res.push_back(intervals[i]); 18 for(j = i; j < intervals.size() && newInterval.end >= intervals[j].start; j ++); 19 if(j != 0 && i != intervals.size()) 20 res.push_back(Interval(min(intervals[i].start, newInterval.start), 21 max(intervals[j - 1].end, newInterval.end))); 22 else 23 res.push_back(newInterval); 24 for(; j < intervals.size(); j ++) res.push_back(intervals[j]); 25 return res; 26 } 27 };
先按start排个序,然后慢慢合并。。。
1 /** 2 * Definition for an interval. 3 * struct Interval { 4 * int start; 5 * int end; 6 * Interval() : start(0), end(0) {} 7 * Interval(int s, int e) : start(s), end(e) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<Interval> res; 13 static bool cxompp(const Interval &a, const Interval &b) 14 {return a.start < b.start;} 15 vector<Interval> merge(vector<Interval> &intervals) { 16 if(intervals.size() == 0) return res; 17 sort(intervals.begin(), intervals.end(), cxompp); 18 Interval last = intervals[0]; 19 for(int i = 1; i < intervals.size(); i ++) 20 { 21 if(last.end >= intervals[i].start) 22 last.end = max(last.end, intervals[i].end); 23 else 24 res.push_back(last), last = intervals[i]; 25 } 26 res.push_back(last); 27 return res; 28 } 29 };
Jump Game
维护最大可跳距离,每个位置都枚举一次。
1 class Solution { 2 public: 3 bool canJump(int A[], int n) { 4 if(n == 0) return false; 5 int i, jumpdis; 6 for(i = jumpdis = 0; i < n && jumpdis >= 0; i ++, jumpdis --) 7 jumpdis = max(A[i], jumpdis); 8 return i == n; 9 } 10 };
Spiral Matrix
模拟转一遍吧。写了俩代码,差不多,处理拐弯的方式略有不同。
代码一:
1 class Solution { 2 public: 3 int dx[4] = {0, 1, 0, -1}; 4 int dy[4] = {1, 0, -1, 0}; 5 vector<int> res; 6 bool JudgeValid(int x, int y, 7 vector<vector<bool> > &vis, vector<vector<int> > &matrix) 8 { 9 return x >= 0 && x < matrix.size() && 10 y >= 0 && y < matrix[0].size() && vis[x][y] == false; 11 } 12 vector<int> spiralOrder(vector<vector<int> > &matrix) { 13 int dir, x, y, nx, ny; 14 if(matrix.size() == 0) return res; 15 vector<vector<bool> > vis(matrix.size(), vector<bool>(matrix[0].size(), false)); 16 for(dir = x = y = 0; JudgeValid(x, y, vis, matrix); x = nx, y = ny) 17 { 18 res.push_back(matrix[x][y]); 19 vis[x][y] = true; 20 nx = x + dx[dir]; 21 ny = y + dy[dir]; 22 if(!JudgeValid(nx, ny, vis, matrix)) 23 { 24 dir = (dir + 1) % 4; 25 nx = x + dx[dir]; 26 ny = y + dy[dir]; 27 } 28 } 29 return res; 30 } 31 };
代码二:
1 class Solution { 2 public: 3 int dx[4] = {0, 1, 0, -1}; 4 int dy[4] = {1, 0, -1, 0}; 5 vector<int> res; 6 vector<int> spiralOrder(vector<vector<int> > &matrix) { 7 int dir, x, y, nx, ny; 8 int l, r, u, d; 9 if(matrix.size() == 0) return res; 10 l = u = -1; 11 r = matrix[0].size(); 12 d = matrix.size(); 13 for(dir = x = y = 0; res.size() < matrix.size() * matrix[0].size(); 14 x = nx, y = ny) 15 { 16 res.push_back(matrix[x][y]); 17 nx = x + dx[dir]; 18 ny = y + dy[dir]; 19 if(nx == d || nx == u || ny == r || ny == l) 20 { 21 dir = (dir + 1) % 4; 22 if(dir == 0) l ++, r --, d --; 23 else if(dir == 3) u ++; 24 nx = x + dx[dir]; 25 ny = y + dy[dir]; 26 } 27 } 28 return res; 29 } 30 };
Maximum Subarray
最大子串和,子串要求至少包含一个数字。
一个变量 sum 表示当前求得的子串和,当 sum 小于0时,对后面的子串没有贡献,则把 sum 置零,中间处理一下要求至少包含一个数字的要求即可。
1 class Solution { 2 public: 3 int maxSubArray(int A[], int n) { 4 int ans = A[0], sum = 0; 5 for(int i = 0; i < n; i ++) 6 { 7 sum += A[i]; 8 if(sum < 0) sum = 0, ans = max(ans, A[i]); 9 else ans = max(ans, sum); 10 } 11 return ans; 12 } 13 };
N-Queens II
题目没说 n 的取值范围,就不用 位运算 做标记了。
老老实实开三个 bool 数组,一个标记纵列,另外两个标记两个斜列,一行一行DFS。
1 class Solution { 2 public: 3 vector<bool> col, lc, rc; 4 int ans; 5 void DFS(int cur, int n) 6 { 7 if(cur == n) 8 { 9 ans ++; 10 return; 11 } 12 for(int i = 0; i < n; i ++) 13 { 14 if(!col[i] && !lc[n - cur - 1 + i] && !rc[cur + i]) 15 { 16 col[i] = lc[n - cur - 1 + i] = rc[cur + i] = true; 17 DFS(cur + 1, n); 18 col[i] = lc[n - cur - 1 + i] = rc[cur + i] = false; 19 } 20 } 21 } 22 int totalNQueens(int n) { 23 ans = 0; 24 col.resize(n, 0); 25 lc.resize(n << 1, 0); 26 rc.resize(n << 1, 0); 27 DFS(0, n); 28 return ans; 29 } 30 };
同上
1 class Solution { 2 public: 3 vector<string> tmp; 4 vector<vector<string> > res; 5 vector<bool> col, lc, rc; 6 void DFS(int cur, int n) 7 { 8 if(cur == n) 9 { 10 res.push_back(tmp); 11 return; 12 } 13 string now(n, '.'); 14 for(int i = 0; i < n; i ++) 15 { 16 if(!col[i] && !lc[n - cur - 1 + i] && !rc[cur + i]) 17 { 18 col[i] = lc[n - cur - 1 + i] = rc[cur + i] = true; 19 now[i] = 'Q'; 20 tmp.push_back(now); 21 DFS(cur + 1, n); 22 tmp.pop_back(); 23 now[i] = '.'; 24 col[i] = lc[n - cur - 1 + i] = rc[cur + i] = false; 25 } 26 } 27 } 28 vector<vector<string> > solveNQueens(int n) { 29 col.resize(n, 0); 30 lc.resize(n << 1, 0); 31 rc.resize(n << 1, 0); 32 DFS(0, n); 33 return res; 34 } 35 };
Pow(x, n)
很多人用特判错过了 n = -2147483648 这么优美的 trick,而不特判的话,似乎只能 long long 了。
经典的快速幂,用二进制理解也好,用折半理解也好,网上很多资料。
1 class Solution { 2 public: 3 double pow(double x, int n) { 4 double res = 1; 5 long long nn = n; 6 if(nn < 0) x = 1 / x, nn = -nn; 7 while(nn) 8 { 9 if(nn & 1) res *= x; 10 x *= x; 11 nn >>= 1; 12 } 13 return res; 14 } 15 };
Anagrams
这概念以前没听过诶。。题也没看到样例,不知道以后会不会更新,网上查了才明白啥意思。
调换单词字母顺序能一致的单词集合全放进答案。比如有tea, eat, aet,就都要放进答案,有cat, atc,就都要放进答案,而如果孤零零有个dog,没其他可和他一组的,那么就不放进答案。
手写hash能更快些,但是题目没给数据范围,给hash数组定多大都没合理性,干脆用unordered_map好了。
1 class Solution { 2 public: 3 vector<string> res; 4 vector<string> anagrams(vector<string> &strs) { 5 unordered_map<string, int> mp; 6 for(int i = 0; i < strs.size(); i ++) 7 { 8 string tmp = strs[i]; 9 sort(tmp.begin(), tmp.end()); 10 if(!mp.count(tmp)) mp[tmp] = 0; 11 else mp[tmp] ++; 12 } 13 for(int i = 0; i < strs.size(); i ++) 14 { 15 string tmp = strs[i]; 16 sort(tmp.begin(), tmp.end()); 17 if(mp.count(tmp) && mp[tmp] > 0) res.push_back(strs[i]); 18 } 19 return res; 20 } 21 };
四个一组,就地旋转。
1 class Solution { 2 public: 3 void rotate(vector<vector<int> > &matrix) { 4 if(matrix.size() == 0) return; 5 int len = matrix.size(); 6 int lenlimi = len + 1 >> 1; 7 for(int i = 0; i < lenlimi; i ++) 8 for(int j = 0; j < (len & 1 ? lenlimi - 1 : lenlimi); j ++) 9 { 10 int tmp = matrix[i][j]; 11 matrix[i][j] = matrix[len - j - 1][i]; 12 matrix[len - j - 1][i] = matrix[len - i - 1][len - j - 1]; 13 matrix[len - i - 1][len - j - 1] = matrix[j][len - i - 1]; 14 matrix[j][len - i - 1] = tmp; 15 } 16 } 17 };
Permutations II
有重复数字,把数字统计起来好了。因为题目没说数字大小,所以统计用了unordered_map。
也可以把数组排序,DFS时跳过重复的数字。
1 class Solution { 2 public: 3 unordered_map<int, int> mp; 4 vector<int> tmp; 5 vector<vector<int> > res; 6 int numsize; 7 void DFS(int cnt) 8 { 9 if(cnt == numsize) 10 { 11 res.push_back(tmp); 12 } 13 for(unordered_map<int, int>::iterator it = mp.begin(); it != mp.end(); it ++) 14 { 15 if(it->second != 0) 16 { 17 tmp.push_back(it->first); 18 it->second --; 19 DFS(cnt + 1); 20 tmp.pop_back(); 21 it->second ++; 22 } 23 } 24 } 25 vector<vector<int> > permute(vector<int> &num) { 26 numsize = num.size(); 27 for(int i = 0; i < num.size(); i ++) 28 { 29 if(!mp.count(num[i])) mp[num[i]] = 1; 30 else mp[num[i]] ++; 31 } 32 DFS(0); 33 return res; 34 } 35 };
Permutations
虽然题目没说有没有重复数字。。既然 Permutations II 说有了,那就当这个没有吧。
传统DFS。
1 class Solution { 2 public: 3 vector<vector<int> > res; 4 void DFS(int cur, vector<int> &num) 5 { 6 if(cur == num.size()) 7 { 8 res.push_back(num); 9 return; 10 } 11 for(int i = cur; i < num.size(); i ++) 12 { 13 swap(num[cur], num[i]); 14 DFS(cur + 1, num); 15 swap(num[cur], num[i]); 16 } 17 } 18 vector<vector<int> > permute(vector<int> &num) { 19 DFS(0, num); 20 return res; 21 } 22 };
Jump Game II
维护一步最远到达的位置,到达这个位置之前的位置需要的步数都是一样的,到达这个位置的时候,下一步的最远位置已经更新完毕。
1 class Solution { 2 public: 3 int jump(int A[], int n) { 4 int nex = 0, pace = 0, far = 0; 5 for(int i = 0; i <= nex && i < n - 1; i ++) 6 { 7 far = max(far, A[i] + i); 8 if(i == nex) 9 { 10 pace ++; 11 nex = far; 12 } 13 } 14 return pace; 15 } 16 };
同步扫描两个字符串,每当 p 遇到 '*' ,记录s和p的当前扫描位置,当 s 与 p 不匹配时,跑扫描指针回到 '*' 后一个字符, s 扫描指针回到上次遇到 '*' 之后与 p 开始匹配位置的下一个位置。
1 class Solution { 2 public: 3 bool isMatch(const char *s, const char *p) { 4 int last_star = -1, last_s = -1, i, j; 5 for(i = j = 0; s[i]; ) 6 { 7 if(s[i] == p[j] || p[j] == '?') i ++, j ++; 8 else if(p[j] == '*') last_star = ++ j, last_s = i; 9 else if(last_star != -1) i = ++ last_s, j = last_star; 10 else return false; 11 } 12 while(p[j] == '*') j ++; 13 return !s[i] && !p[j]; 14 } 15 };
Multiply Strings
翻转num1和num2,大整数乘法,把结果再翻转。注意 int 和 char 的转换。
1 class Solution { 2 public: 3 string multiply(string num1, string num2) { 4 string ans(num1.length() + num2.length() + 1, 0); 5 reverse(num1.begin(), num1.end()); 6 reverse(num2.begin(), num2.end()); 7 int cur = 0, i, j, k; 8 for(i = 0; i < num1.length(); i ++) 9 { 10 for(j = 0; j < num2.length(); j ++) 11 { 12 ans[i + j] += cur + (num1[i] - '0') * (num2[j] - '0'); 13 cur = ans[i + j] / 10; 14 ans[i + j] %= 10; 15 } 16 for(k = i + j; cur; k ++) 17 { 18 ans[k] += cur; 19 cur = ans[k] / 10; 20 ans[k] %= 10; 21 } 22 } 23 for(k = ans.length() - 1; k > 0 && ans[k] == 0; k --); 24 ans.resize(k + 1); 25 for(int i = 0; i < ans.length(); i ++) ans[i] += '0'; 26 reverse(ans.begin(), ans.end()); 27 return ans; 28 } 29 };
Trapping Rain Water
对于每个位置,取这个位置“左边最高的”和“右边最高的”的”较低者“,如果“较低者”比这个位置高,则这个位置存水高度为“较低者”减该位置高度。
1 class Solution { 2 public: 3 int trap(int A[], int n) { 4 vector<int> pre; 5 int i, maxheight, ans; 6 for(i = maxheight = 0; i < n; i ++) 7 { 8 maxheight = max(A[i], maxheight); 9 pre.push_back(maxheight); 10 } 11 for(maxheight = ans = 0, i = n - 1; i > 0; i --) 12 { 13 maxheight = max(A[i], maxheight); 14 ans += max(0, min(pre[i] - A[i], maxheight - A[i])); 15 } 16 return ans; 17 } 18 };
First Missing Positive
题目要求时间O(n),空间O(1),经分析,不得不破坏原数组 A。
方法一:
剔除非整数,把原数组 A 当作存在标记,存在的数 x 则 A[x-1]取负数。
1 class Solution { 2 public: 3 int firstMissingPositive(int A[], int n) { 4 int i, j; 5 for(i = j = 0; i < n; i ++) 6 if(A[i] > 0) A[j ++] = A[i]; 7 for(i = 0; i < j; i ++) 8 if(abs(A[i]) <= j) A[abs(A[i]) - 1] = -abs(A[abs(A[i]) - 1]); 9 for(i = 0; i < j; i ++) 10 if(A[i] > 0) return i + 1; 11 return j + 1; 12 } 13 };
方法二:
把出现的符合范围的数swap到下标和数对应的位置,再次遍历,数和下标不对应则是第一个没出现的数。注意处理有重复数字。
1 class Solution { 2 public: 3 int firstMissingPositive(int A[], int n) { 4 int i; 5 for(i = 0; i < n; i ++) 6 while(A[i] <= n && A[i] > 0 && A[i] != i + 1 && A[A[i] - 1] != A[i]) 7 swap(A[i], A[A[i] - 1]); 8 for(i = 0; i < n; i ++) 9 if(A[i] != i + 1) return i + 1; 10 return i + 1; 11 } 12 };
Combination Sum
基础DFS
1 class Solution { 2 public: 3 vector<int> tmp; 4 vector<vector<int> > ans; 5 void DFS(vector<int> &num, int ith, int now, int target) 6 { 7 if(now == target) 8 { 9 ans.push_back(tmp); 10 return; 11 } 12 if(ith == num.size()) return; 13 int cnt = 0; 14 while(now <= target) 15 { 16 DFS(num, ith + 1, now, target); 17 now += num[ith]; 18 cnt ++; 19 tmp.push_back(num[ith]); 20 } 21 while(cnt --) tmp.pop_back(); 22 } 23 vector<vector<int> > combinationSum(vector<int> &candidates, int target) { 24 sort(candidates.begin(), candidates.end()); 25 DFS(candidates, 0, 0, target); 26 return ans; 27 } 28 };
Combination Sum II
如果一个数没有被用,那么后面重复的这个数就别用,避免重复解。
1 class Solution { 2 public: 3 vector<int> tmp; 4 vector<vector<int> > ans; 5 void DFS(vector<int> &num, int ith, int now, int target) 6 { 7 if(now == target) 8 { 9 ans.push_back(tmp); 10 return; 11 } 12 if(ith == num.size()) return; 13 int nex; 14 for(nex = ith + 1; nex < num.size() && num[nex] == num[ith]; nex ++); 15 DFS(num, nex, now, target); 16 if(num[ith] + now <= target) 17 { 18 now += num[ith]; 19 tmp.push_back(num[ith]); 20 DFS(num, ith + 1, now, target); 21 tmp.pop_back(); 22 } 23 } 24 vector<vector<int> > combinationSum2(vector<int> &num, int target) { 25 sort(num.begin(), num.end()); 26 DFS(num, 0, 0, target); 27 return ans; 28 } 29 };
Count and Say
直接模拟,递推。
1 class Solution { 2 public: 3 string countAndSay(int n) { 4 string f[2]; 5 f[0] = "1"; 6 for(int i = 1; i < n; i ++) 7 { 8 f[i & 1].clear(); 9 for(int j = 0; j < f[i & 1 ^ 1].length();) 10 { 11 int cnt; 12 char x = f[i & 1 ^ 1][j]; 13 for(cnt = 0; j < f[i & 1 ^ 1].length() && f[i & 1 ^ 1][j] == x; cnt ++, j ++); 14 f[i & 1] += '0' + cnt; 15 f[i & 1] += x; 16 } 17 } 18 return f[n & 1 ^ 1]; 19 } 20 };
Sudoku Solver
这道题考察回溯和数独结果的判断。ACM做过,就直接拿dancing links代码了,4ms。
关于dancing links,对于面试题来说变态了些,应该不至于考察。
1 class Solution { 2 public: 3 int rw[10], cl[10], in[10], RW[81], CL[81], IN[81], goal; 4 char buf[100]; 5 void Mark(int i, int num) 6 { 7 rw[RW[i]] ^= 1 << num; 8 cl[CL[i]] ^= 1 << num; 9 in[IN[i]] ^= 1 << num; 10 } 11 void init() 12 { 13 int i; 14 for(i = 0; i < 10; ++ i) 15 cl[i] = rw[i] = in[i] = 0; 16 for(i = goal = 0; buf[i]; ++ i) 17 goal += buf[i] == '.'; 18 for(i = 0; i < 81; ++ i) 19 { 20 RW[i] = i / 9, CL[i] = i % 9, IN[i] = i / 3 % 3 + i / 27 * 3; 21 if(buf[i] != '.') 22 Mark(i, buf[i] - '1'); 23 } 24 } 25 inline int Judge(int i, int num) 26 {return ~(rw[RW[i]] | cl[CL[i]] | in[IN[i]]) & (1 << num);} 27 int Oper(int sx, int k, int cur) 28 { 29 Mark(sx, k), buf[sx] = k + '1'; 30 if(dfs(cur + 1)) return 1; 31 Mark(sx, k), buf[sx] = '.'; 32 return 0; 33 } 34 int JudgeRWCLIN(int cur) 35 { 36 int i, j, k, x, cnt, sx; 37 for(i = 0; i < 9; ++ i) 38 for(k = 0; k < 9; ++ k) 39 { 40 if(~rw[i] & (1 << k)) 41 { 42 for(j = cnt = 0; j < 9; ++ j) 43 { 44 x = i * 9 + j; 45 if(buf[x] == '.' && Judge(x, k)) ++ cnt, sx = x; 46 } 47 if(cnt == 0) return 0; 48 else if(cnt == 1) 49 return Oper(sx, k, cur); 50 } 51 if(~cl[i] & (1 << k)) 52 { 53 for(j = cnt = 0; j < 9; ++ j) 54 { 55 x = j * 9 + i; 56 if(buf[x] == '.' && Judge(x, k)) ++ cnt, sx = x; 57 } 58 if(cnt == 0) return 0; 59 else if(cnt == 1) 60 return Oper(sx, k, cur); 61 } 62 if(~in[i] & (1 << k)) 63 { 64 for(j = cnt = 0; j < 9; ++ j) 65 { 66 x = i / 3 * 27 + j / 3 * 9 + i % 3 * 3 + j % 3; 67 if(buf[x] == '.' && Judge(x, k)) ++ cnt, sx = x; 68 } 69 if(cnt == 0) return 0; 70 else if(cnt == 1) 71 return Oper(sx, k, cur); 72 } 73 } 74 return 2; 75 } 76 77 78 bool dfs(int cur) 79 { 80 int i, j, num, cnt; 81 if(cur == goal) return true; 82 for(i = 0; i < 81; ++ i) 83 if(buf[i] == '.') 84 { 85 for(j = cnt = 0; j < 9; ++ j) 86 if(Judge(i, j)) ++ cnt, num = j; 87 if(cnt == 0) return false; 88 if(cnt == 1) 89 return Oper(i, num, cur); 90 } 91 if((num = JudgeRWCLIN(cur)) == 0) return false; 92 else if(num == 1) return true; 93 for(i = 0; i < 81; ++ i) 94 if(buf[i] == '.') 95 { 96 for(j = 0; j < 9; ++ j) 97 if(Judge(i, j)) 98 { 99 Mark(i, j), buf[i] = j + '1'; 100 if(dfs(cur + 1)) return true; 101 Mark(i, j), buf[i] = '.'; 102 } 103 } 104 return false; 105 } 106 void solveSudoku(vector<vector<char> > &board) { 107 int site = 0; 108 for(int i = 0; i < 9; i ++) 109 for(int j = 0; j < 9; j ++) 110 buf[site ++] = board[i][j]; 111 init(); 112 dfs(0); 113 site = 0; 114 for(int i = 0; i < 9; i ++) 115 for(int j = 0; j < 9; j ++) 116 board[i][j] = buf[site ++]; 117 } 118 };
Valid Sudoku
行列九宫格都判断一下。
1 class Solution { 2 public: 3 bool isValidSudoku(vector<vector<char> > &board) { 4 bool flag[3][9][9]; 5 memset(flag, false, sizeof(flag)); 6 for(int i = 0; i < 9; i ++) 7 { 8 for(int j = 0; j < 9; j ++) 9 { 10 if(board[i][j] != '.') 11 { 12 int x = board[i][j] - '1'; 13 if(flag[0][i][x] == true) return false; 14 flag[0][i][x] = true; 15 if(flag[1][j][x] == true) return false; 16 flag[1][j][x] = true; 17 if(flag[2][i / 3 * 3 + j / 3][x] == true) return false; 18 flag[2][i / 3 * 3 + j / 3][x] = true; 19 } 20 } 21 } 22 return true; 23 } 24 };
Search Insert Position
二分
1 class Solution { 2 public: 3 int searchInsert(int A[], int n, int target) { 4 int left, right, mid; 5 for(left = 0, right = n; left < right; ) 6 { 7 mid = left + right >> 1; 8 if(A[mid] == target) return mid; 9 if(A[mid] > target) right = mid; 10 else left = mid + 1; 11 } 12 return left; 13 } 14 };
Search for a Range
二分,容易错。可以用lower_bound和upper_bound。
手工代码:
1 class Solution { 2 public: 3 vector<int> searchRange(int A[], int n, int target) { 4 int left, right, mid, l, r; 5 for(left = 0, right = n; left < right; ) 6 { 7 mid = left + right >> 1; 8 if(A[mid] >= target) right = mid; 9 else left = mid + 1; 10 } 11 l = left; 12 for(left = 0, right = n; left < right; ) 13 { 14 mid = left + right >> 1; 15 if(A[mid] > target) right = mid; 16 else left = mid + 1; 17 } 18 r = left - 1; 19 if(l >= n || A[l] != target) return vector<int>(2, -1); 20 vector<int> ans = {l, r}; 21 return ans; 22 } 23 };
STL:
1 class Solution { 2 public: 3 vector<int> searchRange(int A[], int n, int target) { 4 int l = lower_bound(A, A + n, target) - A; 5 int r = upper_bound(A, A + n, target) - A; 6 if(l == n || A[l] != target) return vector<int>(2, -1); 7 vector<int> ans = {l, r - 1}; 8 return ans; 9 } 10 };
Search in Rotated Sorted Array
还是二分,但是要判断一下 mid 在哪部分里。
1 class Solution { 2 public: 3 int search(int A[], int n, int target) { 4 int left = 0, right = n - 1, mid; 5 while(left < right) 6 { 7 mid = left + right >> 1; 8 if(A[mid] == target) return mid; 9 if(A[mid] >= A[left]) 10 { 11 if(target < A[mid] && A[left] <= target) right = mid; 12 else left = mid + 1; 13 } 14 else 15 { 16 if(target <= A[right] && A[mid] < target) left = mid + 1; 17 else right = mid; 18 } 19 } 20 return A[left] == target ? left : -1; 21 } 22 };
这道题时间限制在O(n),用一个 stack 实现括号配对+统计, 为了方便实现,写成数组的形式。
对不同深度的括号配对统计个数,一层配对成功把该层统计结果加给上一层,这一层清空。
1 class Solution { 2 public: 3 int longestValidParentheses(string s) { 4 vector<int> cnt(1, 0); 5 int i, ans; 6 for(i = ans = 0; i < s.length(); i ++) 7 { 8 if(s[i] == '(') 9 cnt.push_back(0); 10 else 11 { 12 if(cnt.size() > 1) 13 { 14 cnt[cnt.size() - 2] += *cnt.rbegin() + 2; 15 cnt.pop_back(); 16 ans = max(ans, *cnt.rbegin()); 17 } 18 else 19 cnt[0] = 0; 20 } 21 } 22 return ans; 23 } 24 };
从后往前找到第一个非降序的 num[i],再重新从后往前找到第一个比 num[i] 大的,swap(num[i], num[j]),再把 i 之后的排序。
1 class Solution { 2 public: 3 void nextPermutation(vector<int> &num) { 4 int i, j; 5 for(i = num.size() - 2; i >= 0 && num[i] >= num[i + 1]; i --); 6 for(j = num.size() - 1; j > i && num[j] <= num[i]; j --); 7 if(i < j) 8 { 9 swap(num[i], num[j]); 10 sort(num.begin() + i + 1, num.end()); 11 } 12 else 13 reverse(num.begin(), num.end()); 14 } 15 };
Substring with Concatenation of All Words
直观的方法是枚举起点,判断这个起点下的子串是否合法,O(S.length()*L.size())。
其实可以把 S 分成 L[0].length() 个序列,每个序列都是元素间相隔 L[0].length() 的“string开头”,这些序列互不相干。
如下表,假设 L[0].length()=4,第一行数字为分组组号,第二行数字表示 S 的序号。
(0) | (1) | (2) | (3) | (0) | (1) | (2) | (3) | (0) | (1) | (2) | (3) | (0) | (1) | (2) | (3) | (0) | (1) | (2) | (3) | (0) |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
对每个序列,用单调队列的思路来处理,一个一个子串入队,当包含了 L 中所有 string 的时候,保存答案。当新元素入队时超出统计允许时——即 L 中有 3 个 "str", 而这时候遇到第 4 个——则开始出队,一直出到队列里不足 3 个 "str",然后继续。
这样复杂度为O(L[0].length() * S.length() / L[0].length()) = O(S.length())。目前提交结果是180ms。
1 class Solution { 2 public: 3 vector<int> findSubstring(string S, vector<string> &L) { 4 vector<int> ans; 5 if(L.size() == 0) return ans; 6 unordered_map<string, int> mp, sum; 7 int llen = L[0].length(), i, front, rear; 8 for(int i = 0; i < L.size(); i ++) 9 { 10 if(!mp.count(L[i])) mp[L[i]] = 1; 11 else mp[L[i]] ++; 12 } 13 for(i = 0; i < llen; i ++) 14 { 15 sum = mp; 16 int cnt = 0; 17 for(front = rear = i; front + llen <= S.length(); front += llen) 18 { 19 string tmp = S.substr(front, llen); 20 if(sum.count(tmp)) 21 { 22 if(sum[tmp] > 0) 23 { 24 sum[tmp] --; 25 cnt ++; 26 if(cnt == L.size()) 27 { 28 ans.push_back(rear); 29 } 30 } 31 else 32 { 33 while(sum[tmp] == 0) 34 { 35 string ntmp = S.substr(rear, llen); 36 sum[ntmp] ++; 37 cnt --; 38 rear += llen; 39 } 40 sum[tmp] --; 41 cnt ++; 42 if(cnt == L.size()) 43 { 44 ans.push_back(rear); 45 } 46 } 47 } 48 else 49 { 50 while(rear < front) 51 { 52 string ntmp = S.substr(rear, llen); 53 sum[ntmp] ++; 54 cnt --; 55 rear += llen; 56 } 57 rear += llen; 58 cnt = 0; 59 } 60 } 61 } 62 return ans; 63 } 64 };
Divide Two Integers
假设 dividend 与 divisor 正负一致, divisor^(2^n) 为最接近 dividend 的 divisor 的幂,那么令 newdividend = dividend - divisor^(2^n),ans = ans + 2^n,问题就更新为 newdividend 除以 divisor,如此迭代。用 divisor^(2^n) 是因为 divisor 不停地辗转加自己就可以得到了。
有 -2147483648 这样的极限数据,因为 int 范围是 -2147483648~+2147483647,发现负数比正数范围“多1”,干脆把所有数都转成负数算,这样就避免用 long long 了。最后考察一下flag。
(如果转成正数的话,int 的 -(-2147483648)还是 -2147483648。。)
1 class Solution { 2 public: 3 int divide(int dividend, int divisor) { 4 bool flag = false; 5 if(divisor > 0) divisor = -divisor, flag ^= true; 6 if(dividend > 0) dividend = -dividend, flag ^= true; 7 int ans = 0, res = divisor, ex = 1; 8 if(divisor < dividend) return 0; 9 while(res >= dividend - res) 10 { 11 res += res; 12 ex += ex; 13 } 14 while(res <= divisor && dividend) 15 { 16 if(res >= dividend) 17 { 18 dividend -= res; 19 ans += ex; 20 } 21 res >>= 1; 22 ex >>= 1; 23 } 24 return flag ? -ans : ans; 25 } 26 };
Implement strStr()
KMP。
1 class Solution { 2 public: 3 char *strStr(char *haystack, char *needle) { 4 int hlen = (int)strlen(haystack), nlen = (int)strlen(needle); 5 if(nlen == 0) return haystack; 6 vector<int> next(nlen + 1); 7 next[0] = -1; 8 for(int i = 0, j = -1; i < nlen;) 9 { 10 if(j == -1 || needle[i] == needle[j]) 11 { 12 i ++, j ++; 13 if(needle[i] != needle[j]) next[i] = j; 14 else next[i] = next[j]; 15 } 16 else j = next[j]; 17 } 18 for(int i = 0, j = 0; i < hlen;) 19 { 20 if(j == -1 || haystack[i] == needle[j]) 21 i ++, j ++; 22 else j = next[j]; 23 if(j == nlen) return haystack + i - j; 24 } 25 return NULL; 26 } 27 };
Remove Element
两个游标 i, j 异步挪动,把不等于给定值的数往前挪。
1 class Solution { 2 public: 3 int removeElement(int A[], int n, int elem) { 4 int i, j; 5 for(i = j = 0; i < n; i ++) 6 if(A[i] != elem) A[j ++] = A[i]; 7 return j; 8 } 9 };
Remove Duplicates from Sorted Array
两个游标 i, j 异步挪动,不重复值往前挪。
1 class Solution { 2 public: 3 int removeDuplicates(int A[], int n) { 4 int i, j; 5 for(i = j = 1; i < n; i ++) 6 if(A[i] != A[i - 1]) A[j ++] = A[i]; 7 return n ? j : 0; 8 } 9 };
用头插法来做的,顺序插入到首节点之后,就反转了。每 k 个节点处理之后,把首节指针点移动到下 k 个的开头。最后面不足 k 个的话,再反转回来。
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 int Reverse(ListNode *&pre, ListNode *&p, int k) 12 { 13 int i; 14 ListNode *nex, *tmp; 15 for(i = 1; p != NULL; i ++, p = tmp) 16 { 17 if(i == 1) nex = p; 18 tmp = p->next; 19 p->next = pre->next; 20 pre->next = p; 21 if(i == k) i = 0, pre = nex; 22 } 23 nex->next = NULL; 24 return i; 25 } 26 ListNode *reverseKGroup(ListNode *head, int k) { 27 if(head == NULL) return NULL; 28 ListNode *tmphead = new ListNode(0), *pre = tmphead, *p = head; 29 tmphead->next = head; 30 if(Reverse(pre, p, k) != 1) 31 { 32 p = pre->next; 33 Reverse(pre, p, k); 34 } 35 return tmphead->next; 36 } 37 };
Reverse Nodes in k-Group的简化版。
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 *swapPairs(ListNode *head) { 12 if(head == NULL) return NULL; 13 ListNode *tmphead = new ListNode(0), *pre = tmphead, *p = head, *tmp, *nex; 14 tmphead->next = head; 15 for(int i = 0; p != NULL; i ++, p = tmp) 16 { 17 if(i & 1 ^ 1) nex = p; 18 tmp = p->next; 19 p->next = pre->next; 20 pre->next = p; 21 if(i & 1) pre = nex; 22 } 23 nex->next = NULL; 24 return tmphead->next; 25 } 26 };
Merge k Sorted Lists
一个堆(这里用了优先级队列),把所有 list 的首元素放堆里,O(logn)取得最小值插入新队列,异步推进。
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 struct comp 12 { 13 bool operator()(ListNode *a,ListNode *b) 14 {return a->val > b->val;} 15 }; 16 ListNode *mergeKLists(vector<ListNode *> &lists) { 17 ListNode *tmphead = new ListNode(0), *p = tmphead; 18 priority_queue<ListNode*, vector<ListNode*>, comp> q; 19 for(int i = 0; i < lists.size(); i ++) 20 if(lists[i] != NULL) q.push(lists[i]); 21 while(!q.empty()) 22 { 23 p->next = q.top(); 24 p = p->next; 25 q.pop(); 26 if(p ->next != NULL) q.push(p->next); 27 } 28 return tmphead->next; 29 } 30 };
Generate Parentheses
DFS,保持当前右括号不多于左括号。
1 class Solution { 2 public: 3 string tmp; 4 vector<string> ans; 5 void DFS(int left, int right, int n) 6 { 7 if(left == right && left == n) 8 { 9 ans.push_back(tmp); 10 return; 11 } 12 if(left < n) 13 { 14 tmp[left + right] = '('; 15 DFS(left + 1, right, n); 16 } 17 if(right < left) 18 { 19 tmp[left + right] = ')'; 20 DFS(left, right + 1, n); 21 } 22 } 23 vector<string> generateParenthesis(int n) { 24 tmp.resize(n << 1); 25 DFS(0, 0, n); 26 return ans; 27 } 28 };
用栈配对。
1 class Solution { 2 public: 3 bool isValid(string s) { 4 stack<char> st; 5 for(int i = 0; i < s.length(); i ++) 6 { 7 switch(s[i]) 8 { 9 case '(': st.push('('); break; 10 case '[': st.push('['); break; 11 case '{': st.push('{'); break; 12 case ')': 13 if(st.empty() || st.top() != '(') return false; 14 st.pop(); break; 15 case ']': 16 if(st.empty() || st.top() != '[') return false; 17 st.pop(); break; 18 case '}': 19 if(st.empty() || st.top() != '{') return false; 20 st.pop(); break; 21 22 } 23 } 24 return st.empty(); 25 } 26 };
Remove Nth Node From End of List
两个指针相隔 n 距离,前面的指针到了末尾,后面的指针就是删除的位置。
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 *removeNthFromEnd(ListNode *head, int n) { 12 ListNode *pre, *slow, *quick; 13 ListNode *newhead = new ListNode(0); 14 newhead->next = head; 15 int i = 0; 16 for(pre = slow = quick = newhead; quick != NULL; i ++) 17 { 18 pre = slow; 19 if(i >= n) slow = slow->next; 20 quick = quick->next; 21 } 22 pre->next = slow->next; 23 free(slow); 24 return newhead->next; 25 } 26 };
Letter Combinations of a Phone Number
基础DFS。
1 class Solution { 2 public: 3 const vector<string> v = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; 4 vector<string> ans; 5 string tmp; 6 void DFS(int cur, string d) 7 { 8 if(cur == d.length()) 9 { 10 ans.push_back(tmp); 11 return; 12 } 13 for(int i = 0; i < v[d[cur] - '0'].length(); i ++) 14 { 15 tmp[cur] = v[d[cur] - '0'][i]; 16 DFS(cur + 1, d); 17 } 18 } 19 vector<string> letterCombinations(string digits) { 20 tmp.resize(digits.length()); 21 DFS(0, digits); 22 return ans; 23 } 24 };
4Sum
尝试了O(n^2)的,但是应该常数很大吧,超时了。就是哈希存两两的和,然后通过查哈希表找到 两两+两两,要判断数字重复情况。这题数据量挺大的,O(n^3)如果用不太好的方式实现的话也会超。
O(n^3)方法:先对num排序,然后从两头枚举两个数,O(n^2),后两个数在前两个数之间的两端开始,和小了左边的往右,和大了右边的往左调整,O(n),总共O(n^3)。
1 class Solution { 2 public: 3 vector<vector<int> > ans; 4 vector<vector<int> > fourSum(vector<int> &num, int target) { 5 if(num.size() < 4) return ans; 6 sort(num.begin(), num.end()); 7 for(int left = 0; left < num.size() - 3;) 8 { 9 for(int right = num.size() - 1; right > left + 2;) 10 { 11 int ml = left + 1, mr = right - 1; 12 while(ml < mr) 13 { 14 int tmpsum = num[left] + num[right] + num[ml] + num[mr]; 15 if(tmpsum > target) mr --; 16 else if(tmpsum < target) ml ++; 17 else 18 { 19 vector<int> tmp = {num[left], num[ml], num[mr], num[right]}; 20 ans.push_back(tmp); 21 ml ++; 22 mr --; 23 } 24 for(; ml != left + 1 && ml < mr && num[ml] == num[ml - 1]; ml ++); 25 for(; mr != right - 1 && ml < mr && num[mr] == num[mr + 1]; mr --); 26 } 27 for(right --; right > left + 2 && num[right] == num[right + 1]; right --); 28 } 29 for(left ++; left < num.size() - 3 && num[left] == num[left - 1]; left ++); 30 } 31 return ans; 32 } 33 };
3Sum Closest
O(n^2),先排序,枚举第一个数,后两个数一个在第一个数后边一个开始,一个从 末尾开始,和4Sum类似调整。
1 class Solution { 2 public: 3 int threeSumClosest(vector<int> &num, int target) { 4 bool findans = false; 5 int ans; 6 sort(num.begin(), num.end()); 7 for(int i = 0; i < num.size(); i ++) 8 { 9 for(int left = i + 1, right = num.size() - 1; left < right;) 10 { 11 int tmpsum = num[i] + num[left] + num[right]; 12 if(tmpsum > target) right --; 13 else if(tmpsum < target) left ++; 14 else return tmpsum; 15 if(!findans || abs(tmpsum - target) < abs(ans - target)) 16 ans = tmpsum, findans = true; 17 } 18 } 19 return ans; 20 } 21 };
3Sum
同上。
1 class Solution { 2 public: 3 vector<vector<int> > ans; 4 vector<vector<int> > threeSum(vector<int> &num) { 5 if(num.size() < 3) return ans; 6 sort(num.begin(), num.end()); 7 for(int i = 0; i < num.size();) 8 { 9 for(int left = i + 1, right = num.size() - 1; left <right;) 10 { 11 int tmpsum = num[i] + num[left] + num[right]; 12 if(tmpsum < 0) left ++; 13 else if(tmpsum > 0) right --; 14 else 15 { 16 vector<int> tmp = {num[i], num[left], num[right]}; 17 ans.push_back(tmp); 18 left ++; 19 right --; 20 } 21 for(; left != i + 1 && left < right && num[left] == num[left - 1]; left ++); 22 for(; right != num.size() - 1 && left < right && num[right] == num[right + 1]; right --); 23 } 24 for(i ++; i < num.size() && num[i] == num[i - 1]; i ++); 25 } 26 return ans; 27 } 28 };
Longest Common Prefix
一个一个扫
1 class Solution { 2 public: 3 string ans; 4 string longestCommonPrefix(vector<string> &strs) { 5 if(strs.size() == 0) return ans; 6 if(strs.size() == 1) return strs[0]; 7 for(int j = 0; ; j ++) 8 { 9 for(int i = 1; i < strs.size(); i ++) 10 if(strs[i].size() == j || strs[i][j] != strs[i - 1][j]) return ans; 11 ans += strs[0][j]; 12 } 13 return ans; 14 } 15 };
各有各的方法,重点是记录“上一个”数比“这个”数大或小,来确定谁减谁。基本是右结合的,所以从后往前扫好处理些。
class Solution { public: int ro[128]; int romanToInt(string s) { ro['I'] = 1; ro['V'] = 5; ro['X'] = 10; ro['L'] = 50; ro['C'] = 100; ro['D'] = 500; ro['M'] = 1000; int ans = -1, last; for(int i = s.length() - 1; i >= 0; i --) { if(ans == -1) ans = ro[s[i]]; else { if(last > ro[s[i]]) ans -= ro[s[i]]; else ans += ro[s[i]]; } last = ro[s[i]]; } return ans; } };
Integer to Roman
每个十进制位格式是一样的,只是字母替换一下。
1 class Solution { 2 public: 3 vector<string> table = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; 4 string ro = "IVXLCDM"; 5 char convert(char x, int i) 6 { 7 if(x == 'I') return ro[i]; 8 if(x == 'V') return ro[i + 1]; 9 if(x == 'X') return ro[i + 2]; 10 } 11 string intToRoman(int num) { 12 string ans; 13 for(int i = 0; num; i += 2, num /= 10) 14 { 15 int x = num % 10; 16 string tmp = table[x]; 17 for(int j = 0; j < tmp.size(); j ++) 18 tmp[j] = convert(tmp[j], i); 19 ans = tmp + ans; 20 } 21 return ans; 22 } 23 };
从两端开始枚举,较高的挡板往中间枚举的话一定无法得到更优解,故反复从较低挡板向中间枚举,O(n)。
1 class Solution { 2 public: 3 int maxArea(vector<int> &height) { 4 int left = 0, right = height.size() - 1, ans = -1; 5 while(left < right) 6 { 7 ans = max(ans, min(height[left], height[right]) * (right - left)); 8 if(height[left] < height[right]) left ++; 9 else right --; 10 } 11 return ans; 12 } 13 };
Regular Expression Matching
每遇到一个 '*' ,问题都会出现分枝,需要用到栈或者递归。
没有 '*' 的情况好处理,遇到 '*' 的时候,穷举所有匹配长度。
1 class Solution { 2 public: 3 bool isMatch(const char *s, const char *p) { 4 if(*p == 0) return *s == 0; 5 if(*(p + 1) != '*') 6 { 7 if(*s && (*s == *p || *p == '.')) 8 return isMatch(s + 1, p + 1); 9 return false; 10 } 11 else 12 { 13 for(; *s && (*s == *p || *p == '.'); s ++) 14 if(isMatch(s, p + 2)) return true; 15 return isMatch(s, p + 2); 16 } 17 } 18 };
Palindrome Number
首先处理负数的trick。然后主要思路就是通过 while(...) a = a * 10 + x % 10; 来将 x 翻转。
但是注意到 x 很大的时候,翻转的 x 会超出 int 范围,也许会刚好成为另一个和 a 得出的数相等的正数,所以不能完全翻转后判断,而可以在翻转恰好一半的时候判断。
1 class Solution { 2 public: 3 bool isPalindrome(int x) { 4 if(x < 0) return false; 5 if(x == 0) return true; 6 int a = 0, b = x, cnt = 1; 7 while(x /= 10) cnt ++; 8 for(; b && cnt >= 0; b /= 10, cnt -= 2) 9 { 10 if(cnt == 1) return a == b / 10; 11 else if(cnt == 0) return a == b; 12 a = a * 10 + b % 10; 13 } 14 return false; 15 } 16 };
String to Integer (atoi)
任何类似多符号、符号数字间有空格的小问题都直接输出 0,这就好办了。处理越界用 long long。
1 class Solution { 2 public: 3 int atoi(const char *str) { 4 long long ans = 0; 5 bool flag = false; 6 for(; *str == ' '; str ++); 7 if(*str == '+') str ++; 8 else if(*str == '-') flag = true, str ++; 9 for(; isdigit(*str); str ++) 10 { 11 ans = ans * 10 + *str - '0'; 12 if((flag ? -ans : ans) > INT_MAX) return INT_MAX; 13 else if((flag ? -ans : ans) < INT_MIN) return INT_MIN; 14 } 15 return (int)(flag ? -ans : ans); 16 } 17 };
Reverse Integer
还是关于越界的讨论,不过这道题本身没有设置处理方式,重点在于面试时的交流。
1 class Solution { 2 public: 3 int reverse(int x) { 4 int a = 0; 5 for( int b = x >= 0 ? x : -x; b; b /= 10) 6 a = a * 10 + b % 10; 7 return x >= 0 ? a : -a; 8 } 9 };
题意的 "z" 字形指一列nRows个,然后斜着往右上一格一个回到第一行,然后再一列nRows个。比如nRows=5,如下:
1 | 9 | 17 | 25 | |||||||||||
2 | 8 | 10 | 16 | 18 | 24 | 26 | ||||||||
3 | 7 | 11 | 15 | 19 | 23 | 27 | … | |||||||
4 | 6 | 12 | 14 | 20 | 22 | 28 | 30 | |||||||
5 | 13 | 21 | 29 |
每行字母在原字符串中的间隔是有规律的,虽然两层for循环,但是s中每个字母只访问了一次,O(n)。
1 class Solution { 2 public: 3 string convert(string s, int nRows) { 4 if(nRows == 1) return s; 5 string ans; 6 int a = (nRows << 1) - 2, b = 0; 7 for(int i = 0; i < nRows; i ++, a -= 2, b += 2) 8 { 9 bool flag = false; 10 for(int j = i; j < s.length(); 11 j += flag ? (b ? b : a) : (a ? a : b), flag ^= 1) 12 ans += s[j]; 13 } 14 return ans; 15 } 16 };
网上O(n)的方法是厉害啊。。。简单解释如下:
1、预处理字符串,前后加“哨兵”字符比如 '!',每个字母旁边加辅助字符比如'#',这样例如字符串 s = "ababbcbb" 就变成 tmp = "!#a#b#a#b#b#c#b#b#!"。这样的好处是不用讨论回文串长度的奇偶。
2、对转化后的串,维护一个 center 和一个 reach,center 是当前已发现的 reach 最远的回文串中心位置,reach 是这个回文串最右端的位置,center和reach可初始化为 1,即第一个'#'的位置。
3、维护一个数组 vector<int> r(tmp.length()),r[i] 表示 i 位置为中心的回文串半径。
4、在考察位置 i 的时候,所有 j < i 的 r[j] 都是已知的子问题。如果 i 在 reach 的左边,则 i 包含在以 center 为中心的回文串中,那么可以想到,如果和 i 关于 center 对称位置的 mirrori 为中心的回文串覆盖范围没有到达 center 为中心的回文串边缘,则 i 为中心的回文串肯定和 mirrori 的一样。而如果 mirrori 的回文串到达了边缘甚至超过,或者 i 本来就在 reach 的右边,那么对 i 为中心的回文串进行一次扩展,则结果 或者刚好不扩展,或者一定更新了reach。无论怎样,这里都得到了 r[i]。知道了所有 r[i],答案就出来了。
核心问题在于第4步“对 i 为中心的回文串进行扩展”的复杂度。每次发生“对 i 扩展“,必然是对 reach 的扩展(也可能刚好不扩展,这个不影响复杂度),而 reach 的扩展范围是 tmp 的长度大约 2n,所以总复杂度为 O(n)。
1 class Solution { 2 public: 3 string longestPalindrome(string s) { 4 int center = 1, reach = 1, ansstart = 0, anslength = 0; 5 string tmp = "!#"; 6 for(int i = 0; i < s.length(); i ++) 7 tmp += s[i], tmp += '#'; 8 tmp + '!'; 9 vector<int> r(tmp.length()); 10 for(int i = 2; i < tmp.length(); i ++) 11 { 12 int mirrori = center * 2 - i; 13 r[i] = reach > i ? min(r[mirrori], reach - i) : 0; 14 for(; tmp[i + r[i] + 1] == tmp[i - r[i] - 1]; r[i] ++); 15 if(i + r[i] > reach) reach = i + r[i], center = i; 16 if(r[i] > anslength) 17 { 18 ansstart = i - r[i] >> 1; 19 anslength = r[i]; 20 } 21 } 22 return s.substr(ansstart, anslength); 23 } 24 };
Add Two Numbers
大整数加法的链表版。
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 *addTwoNumbers(ListNode *l1, ListNode *l2) { 12 ListNode *ans = new ListNode(0), *p = ans; 13 int cur = 0; 14 while(l1 != NULL || l2 != NULL || cur) 15 { 16 p->val = (l1 ? l1->val : 0) + (l2 ? l2->val : 0) + cur; 17 cur = p->val / 10; 18 p->val %= 10; 19 if(l1) l1 = l1->next; 20 if(l2) l2 = l2->next; 21 if(l1 || l2 || cur) 22 p->next = new ListNode(0); 23 p = p->next; 24 } 25 return ans; 26 } 27 };
Longest Substring Without Repeating Characters
维护一个不重复字符的区间。
代码一:
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 vector<bool> isin(128, false); 5 int ans = 0; 6 for(int front = 0, rear = 0; front < s.length(); front ++) 7 { 8 if(isin[s[front]]) 9 for(; rear < front && isin[s[front]]; isin[s[rear]] = false, rear ++); 10 isin[s[front]] = true; 11 ans = max(ans, front - rear + 1); 12 } 13 return ans; 14 } 15 };
代码二:
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 vector<int> site(128, -1); 5 int nowstart = -1, ans = 0; 6 for(int i = 0; i < s.length(); i ++) 7 { 8 if(site[s[i]] >= nowstart) 9 nowstart = site[s[i]] + 1; 10 site[s[i]] = i; 11 ans = max(i - nowstart + 1, ans); 12 } 13 return ans; 14 } 15 };
Median of Two Sorted Arrays
如果 A[pa] < B[pb],那么 A[pa] 一定在 A 与 B 合并后的前 pa + pb + 2 个数中。
证明: A 中有 pa + 1 个数 <= A[pa],B 中有小于 pb + 1 个数 <= A[pa],合并后有少于pa + pb + 2 个数 <= A[pa]。
利用这个性质迭代找 A 与 B 合并后的第 k 大数。
1 class Solution { 2 public: 3 int findKth(int A[], int m, int B[], int n, int k) 4 { 5 int pm, pn; 6 while(true) 7 { 8 if(m == 0) return B[k - 1]; 9 if(n == 0) return A[k - 1]; 10 if(k == 1) return min(A[k - 1], B[k - 1]); 11 if(m <= n) pm = min(k >> 1, m), pn = k - pm; 12 else pn = min(k >> 1, n), pm = k - pn; 13 if(A[pm - 1] < B[pn - 1]) A += pm, m -= pm, k -= pm; 14 else if(A[pm - 1] > B[pn - 1]) B += pn, n -= pn, k-= pn; 15 else break; 16 } 17 return A[pm - 1]; 18 } 19 double findMedianSortedArrays(int A[], int m, int B[], int n) { 20 if((m + n) & 1) return findKth(A, m, B, n, (m + n >> 1) + 1); 21 else return (findKth(A, m, B, n, m + n >> 1) + 22 findKth(A, m, B, n, (m + n >> 1) + 1)) * 0.5; 23 } 24 };
Two Sum
哈希存位置,O(n)。
1 class Solution { 2 public: 3 vector<int> twoSum(vector<int> &numbers, int target) { 4 unordered_map<int, int> mp; 5 vector<int> ans; 6 for(int i = 0; i < numbers.size(); i ++) 7 { 8 if(mp.count(target - numbers[i])) 9 { 10 ans.push_back(mp[target - numbers[i]] + 1); 11 ans.push_back(i + 1); 12 break; 13 } 14 if(!mp.count(numbers[i])) mp[numbers[i]] = i; 15 } 16 return ans; 17 } 18 };