笔者按照目录刷题,对于每一道题,力争使用效率最高(时间复杂度最低)的算法,并全部通过C++代码实现AC。(文中计算的复杂度都是最坏情况复杂度)
因为考虑到大部分读者已经在Leetcode浏览过题目了,所以每道题都按照 解题思路、实现代码 的顺序进行讲解。
(笔者目前已刷 120 题,已更新解法 30 题,最近一段时间会频繁更新)可以点击下方链接,直达gitbook:
https://codernie.gitbooks.io/leetcode-solutions/content/
也欢迎大家关注我的同名微信公众号(大雄的学习人生),有问题可以随时后台回复我,多多探讨。
21. Merge Two Sorted Lists
解题思路:
从头至尾一起遍历,每次比较头结点的大小,先添加小的结点,直到某一个链表为空为止,最后再将另一个还不为空的链表添加到末尾即可。
实现代码:
// 21. Merge Two Sorted Lists
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* head = new ListNode(0);
ListNode* p = head;
while (l1 != NULL && l2 != NULL) {
if (l1->val < l2 ->val) {
p->next = l1;
l1 = l1->next;
} else {
p->next = l2;
l2 = l2->next;
}
p = p->next;
}
if (l1 != NULL) {
p->next = l1;
} else if (l2 != NULL) {
p->next = l2;
}
return head->next;
}
22. Generate Parentheses
解题思路:
回溯法,用一个数组记录【当前字符串,左括号数量,右括号数量】,做 2n 次循环,每次循环遍历当前数组,对左括号不足 n 的添加左括号,右括号不足左括号的添加右括号(确保parentheses是有效的)
实现代码:
// 22. Generate Parentheses O(2^N)
vector<string> generateParenthesis(int n) {
if (n == 0) return {};
// nowString, leftCount, rightCount
vector<pair<string, pair<int, int> > > res = {{"", {0, 0}}};
vector<pair<string, pair<int, int> > > linshi;
for (int i = 0; i < 2 * n; i++) {
for (int j = 0; j < res.size(); j++) {
string oldString = res[j].first;
if (res[j].second.first < n) {
linshi.push_back({oldString + '(', {res[j].second.first + 1, res[j].second.second}});
}
if (res[j].second.first > res[j].second.second) {
linshi.push_back({oldString + ')', {res[j].second.first, res[j].second.second + 1}});
}
}
res = linshi;
linshi = {};
}
vector<string> result;
for (int i = 0; i < res.size(); i++) {
result.push_back(res[i].first);
}
return result;
}
23. Merge k Sorted Lists
解题思路:
考虑使用优先队列,这道题相当于21题的加强版,但是如果单纯使用21题的每轮比较法,那么每轮只要要比较 k 次,而我们知道这其中肯定会存在很多重复的比较,所以我们可以使用二叉堆来保存每一轮的比较信息,而在C++中STL已经为我们实现了这种数据结构,那就是优先队列priority_queue,直接调用即可。
实现代码:
// 23. Merge k Sorted Lists
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode *dump = new ListNode(0), *p = dump, *nowPoint;
// val, node
priority_queue<pair<int, ListNode*>, vector<pair<int, ListNode*>>, greater<pair<int, ListNode*>> > pQueue;
for (ListNode* node : lists) {
if (node != NULL)
pQueue.push({node->val, node});
}
while (!pQueue.empty()) {
nowPoint = pQueue.top().second;
p->next = nowPoint;
p = p->next;
pQueue.pop();
if (nowPoint->next != NULL) {
pQueue.push({nowPoint->next->val, nowPoint->next});
}
}
return dump->next;
}
24. Swap Nodes in Pairs
解题思路:
两两交换即可,这里每轮需要变换三个指向,第一个的next要指向第二个的next,第二个的next要指向第一个,前驱的next要指向第二个,每轮过后让 p 指向第一个即可。
实现代码:
// 24. Swap Nodes in Pairs
ListNode* swapPairs(ListNode* head) {
ListNode *dump = new ListNode(0), *p = dump, *first, *second;
dump->next = head;
while (p != NULL && p->next != NULL && p->next->next != NULL) {
first = p->next;
second = p->next->next;
first->next = second->next;
second->next = first;
p->next = second;
p = first;
}
return dump->next;
}
25. Reverse Nodes in k-Group
解题思路:
这题相当于24题的加强版,将24题中的2变成了k,换汤不换药,稍微复杂一点,所以建议把每一步的思路理清楚,不然很容易弄错,大的思路是先检查是否还剩下足够数量的结点;然后进行反向操作:反向操作分为三步,中间结点关系反向,头结点指向处理 和 尾结点指向处理;最后让 p 结点后挪即可。
实现代码:
// 25. Reverse Nodes in k-Group
ListNode* reverseKGroup(ListNode* head, int k) {
if (k == 1) return head;
vector<ListNode*> nodeList;
ListNode *dump = new ListNode(0), *p = dump, *former, *latter, *nextLatter, *q;
dump->next = head;
while (true) {
// check count of left nodes
q = p;
for (int i = 0; i < k + 1; i++) {
if (q != NULL) {
q = q->next;
} else {
return dump->next;
}
}
// deal with medial relations
former = p->next;
latter = former->next;
for (int i = 0; i < k - 1; i++) {
// save latter->next as next latter
nextLatter = latter->next;
// latter->next = former
latter->next = former;
// save latter as next former
former = latter;
latter = nextLatter;
}
// deal with head
q = p->next;
p->next = former;
// deal with tail
q->next = latter;
p = q;
}
return dump->next;
}
26. Remove Duplicates from Sorted Array
解题思路:
按照题意,把数组中不重复出现的数字保存在数组前端即可。
实现代码:
// 26. Remove Duplicates from Sorted Array
int removeDuplicates(vector<int>& nums) {
if (nums.size() == 0) return 0;
int j = 1;
for (int i = 1; i < nums.size(); i++) {
if (nums[i] != nums[i - 1]) {
nums[j++] = nums[i];
}
}
return j;
}
27. Remove Element
解题思路:
这道题和 26 题几乎没有区别,把判断条件稍微改变即可。
实现代码:
// 27. Remove Element
int removeElement(vector<int>& nums, int val) {
int j = 0;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] != val) {
nums[j++] = nums[i];
}
}
return j;
}
28. Implement strStr()
解题思路:
字符串匹配算法,选择比较多,最容易的就是暴力匹配,高阶一点可以使用KMP,这里简单起见,采用暴力解法,嘻嘻。
实现代码:
// 28. Implement strStr()
int strStr(string haystack, string needle) {
if (needle.size() == 0) return 0;
int hLen = haystack.size(), nLen = needle.size();
for (int i = 0; i < hLen; i++) {
if (hLen - i < nLen) {
return -1;
} else {
if (haystack.substr(i, nLen).compare(needle) == 0) {
return i;
}
}
}
return -1;
}
29. Devide Two Integers
解题思路:
这道题如果用减法去实现会造成时间复杂度过高,从而导致时间溢出,所以这里采用位运算法,从 2 的 31 次方开始除,一直除到 2 的 0 次方,为了防止内存溢出,我们把结果和中间变量保存在 long 类型中的。
实现代码:
// 29. Divide Two Integers
int divide(int dividend, int divisor) {
if (dividend > INT32_MAX || dividend < INT32_MIN || divisor > INT32_MAX || divisor < INT32_MIN) return INT32_MAX;
long son = abs((long)divisor), father = abs((long)dividend), res = 0, base = 1, sum = 0;
for (int i = 31; i >= 0; i--) {
if (sum + (son << i) <= father) {
sum += son << i;
res += base << i;
}
}
if ((dividend >= 0 && divisor > 0) || (dividend < 0 && divisor < 0)) {
return (res > INT32_MAX) ? INT32_MAX : res;
} else {
return (-res < INT32_MIN) ? INT32_MAX : -res;
}
}
30. Substring with Concatenation of All Words
解题思路:
使用哈希表存储 words 数组中各个单词的数量,然后使用暴力匹配即可。
实现代码:
// 30. Substring with Concatenation of All Words
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> result;
if (words.size() == 0 || words[0].size() == 0 || s.size() < words.size() * words[0].size()) {
return result;
}
unordered_map<string, int> counts;
for (string word : words) {
if (counts.find(word) == counts.end()) {
counts[word] = 1;
} else {
counts[word]++;
}
}
int wordCount = words.size(), wordLength = words[0].size(), strLength = s.size();
for (int i = 0; i <= strLength - wordLength * wordCount; i++) {
unordered_map<string, int> innerCounts = counts;
bool flag = true;
for (int j = 0; j < wordCount; j++) {
string nowStr = s.substr(i + j * wordLength, wordLength);
if (innerCounts.find(nowStr) == innerCounts.end() || innerCounts[nowStr] == 0) {
flag = false;
break;
} else {
innerCounts[nowStr]--;
}
}
if (flag) {
result.push_back(i);
}
}
return result;
}