21. 合并两个有序链表
跟之前剑指的题是一样的,直接归并。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
auto dummy = new ListNode(-1), tail = dummy;
while (l1 && l2) {
if (l1->val < l2->val) {
tail = tail->next = l1;
l1 = l1->next;
}
else {
tail = tail->next = l2;
l2 = l2->next;
}
}
if (l1) tail->next = l1;
if (l2) tail->next = l2;
return dummy->next;
}
};
class Solution {
public:
vector<string> ans;
vector<string> generateParenthesis(int n) {
dfs(n, 0, 0, "");
return ans;
}
void dfs(int n, int lc, int rc, string seq) {
if (lc == n && rc == n) ans.push_back(seq);
else {
if (lc < n) dfs(n, lc + 1, rc, seq + '(');
if (rc < n && lc > rc) dfs(n, lc, rc + 1, seq + ')');
}
}
};
23. 合并K个升序链表
跟之前两个链表合并的思路一样,还是基于归并的思想,这题用堆进行优化。
所有STL容器和库函数默认使用的是小于号,所以STL中的优先队列默认是大根堆。如果加上greater<>
参数,那么会默认使用大于号。在下面的代码中优先队列会默认用一对小括号表示小于号,并且默认会构造一个大根堆,所以我们把小括号里的关系变一下,最后就可以得到小根堆了。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
//重载(),为了构造下面的小根堆
struct Cmp {
bool operator() (ListNode* a, ListNode* b) {
return a->val > b->val;
}
};
ListNode* mergeKLists(vector<ListNode*>& lists) {
priority_queue<ListNode*, vector<ListNode*>, Cmp> heap;//小根堆
auto dummy = new ListNode(-1), tail = dummy;//虚拟头结点,尾节点
for (auto l : lists) if (l) heap.push(l);//非空的话就插入节点
//k路归并,每次找最小值
while (heap.size()) {
auto t = heap.top();//找堆顶元素(最小值)
heap.pop();//删去堆顶元素
tail->next = t;//将当前节点插到尾节点的后面
tail = tail->next;//更新尾节点
if (t->next) heap.push(t->next);//将下一个节点插入堆中,相当于指针后移一位
}
return dummy->next;
}
};
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
auto dummy = new ListNode(-1);
dummy->next = head;
for (auto cur = dummy; cur->next && cur->next->next; ) {
auto first = cur->next, second = first->next;
cur->next = second;
first->next = second->next;
second->next = first;
cur = first;
}
return dummy->next;
}
};
25. K 个一组翻转链表
链表题指来指去的一定要画图才能清晰。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
auto dummy = new ListNode(-1);
dummy->next = head;
for (auto p = dummy;;) {
auto q = p;
for (int i = 0; i < k && q; i ++ ) q = q->next;
if (!q) break;
auto a = p->next, b = a->next;
for (int i = 0; i < k - 1; i ++ ) {
auto c = b->next;
b->next = a;
a = b, b = c;
}
auto c = p->next;
p->next = a, c->next = b;
p = c;
}
return dummy->next;
}
};
26. 删除排序数组中的重复项
经典的双指针算法,同样也是c++里的unique函数
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int k = 0;//k是前面慢的指针,i是后面快的指针
for (int i = 0; i <nums.size(); i++) {
if (!i || nums[i] != nums[i - 1])//将i = 0的情况特判,所以加上'!i'
nums[k++] = nums[i];
}
return k;
}
};
27. 移除元素
和上一题是完全一样的题目:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int k = 0;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] != val)
nums[k++] = nums[i];
}
return k;
}
};
一道KMP算法的裸题,直接KMP模板。
题解,暴力匹配或者kmp
class Solution {
public:
int strStr(string haystack, string needle) {
int n = haystack.length(), m = needle.length();
for (int i = 0; i < n - m + 1; i++) {
bool ok = true;
for (int j = 0; j < m; j++)
if (haystack[i + j] != needle[j]) {
ok = false;
break;
}
if (ok)
return i;
}
return -1;
}
};
class Solution {
public:
int strStr(string haystack, string needle) {
int n = haystack.length(), m = needle.length();
if (m == 0)
return 0;
vector<int> next(m);
next[0] = -1;
int j = -1;
for (int i = 1; i < m; i++) {
while (j > -1 && needle[i] != needle[j + 1]) j = next[j];
if (needle[i] == needle[j + 1]) j++;
next[i] = j;
}
j = -1;
for (int i = 0; i < n; i++) {
while (j > -1 && haystack[i] != needle[j + 1]) j = next[j];
if (haystack[i] == needle[j + 1]) j++;
if (j == m - 1)
return i - m + 1;
}
return -1;
}
};
29. 两数相除
有点快速幂的思想在里面,主要就是倍增做减法。
class Solution {
public:
int divide(int x, int y) {
typedef long long LL;
vector<LL> exp;//指数项
bool is_minus = false;//负号
if (x < 0 && y > 0 || x > 0 && y < 0) is_minus = true;//异号相除的结果为负
//预处理2*b^i
LL a = abs((LL)x), b = abs((LL)y);//先直接用正数算,到时候结果加上负号就可以
for (LL i = b; i <= a; i = i + i) exp.push_back(i);//i从除数开始,只要i还小于被除数a,就倍增i,然后将指数项插入到exp数组里
LL res = 0;//存答案
for (int i = exp.size() - 1; i >= 0; i -- )//从大到小开始减
if (a >= exp[i]) {//还可以做减法
a -= exp[i];//减去对应的数
res += 1ll << i;//左移一位,相当于乘以2,因为是基于倍增的思想,所以能减多少次,就左移多少次即可得到结果
}
if (is_minus) res = -res;//负号的话就直接取反
if (res > INT_MAX || res < INT_MIN) res = INT_MAX;//处理溢出的情况
return res;
}
};
30. 串联所有单词的子串
困难题,先放着:
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;
if (words.empty()) return res;
int n = s.size(), m = words.size(), w = words[0].size();
unordered_map<string, int> tot;
for (auto& word : words) tot[word] ++ ;
for (int i = 0; i < w; i ++ ) {
unordered_map<string, int> wd;
int cnt = 0;
for (int j = i; j + w <= n; j += w) {
if (j >= i + m * w) {
auto word = s.substr(j - m * w, w);
wd[word] -- ;
if (wd[word] < tot[word]) cnt -- ;
}
auto word = s.substr(j, w);
wd[word] ++ ;
if (wd[word] <= tot[word]) cnt ++ ;
if (cnt == m) res.push_back(j - (m - 1) * w);
}
}
return res;
}
};