LeetCode 21. 合并两个有序链表
- 新建头部的保护结点
dummy
,设置 cur 指针指向dummy
。 - 若当前
l1
指针指向的结点的值val
比l2
指针指向的结点的值val
,则令cur
的next
指针指向l1
,且l1
后移;否则指向l2
,且l2
后移。 - 然后
cur
指针按照上一部设置好的位置后移。 - 循环以上步骤直到
l1
或l2
为空。 - 将剩余的
l1
或l2
接到cur
指针后边。
/**
* 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;
}
};
LeetCode 22. 括号生成
递归
- 左括号的数量不大于 n
- 右括号的数量不大于左括号的数量
符合上述两个条件的括号序列为合法序列,在进行回溯的时候判断序列是否合法即可
class Solution {
public:
vector<string> res;
vector<string> generateParenthesis(int n) {
dfs(n, n, "");
return res;
}
void dfs(int lc, int rc, string path) {
if(!lc && !rc) res.push_back(path);
else {
if(lc < rc) dfs(lc, rc - 1, path + ')');
if(lc) dfs(lc - 1, rc, path + '(');
}
}
};
LeetCode 23. 合并K个排序链表
- 一开始先用小根堆存储k个排序链表的头指针,每次操作后用小根堆维护k个链表当前元素最小的指针,并以指针对应的值进行排序
- 操作过程中,当小根堆不为空时,堆顶元素即当前k个排序链表当前最小的元素的指针t,将该值加入到dummy链表的后面,并把t指针往后走一位,使得t指针指向的值变大,再加入到小根堆中
/**
* 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:
//堆的排序规则
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);
while(heap.size()) {
auto t = heap.top();
heap.pop();
//指向下一个节点,后压入堆中
tail = tail -> next = t;
if(t -> next ) heap.push(t -> next);
}
return dummy -> next;
}
};
LeetCode 24. 两两交换链表中的节点
过程图解,模拟一遍即可
/**
* 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 p = dummy; p -> next && p -> next -> next;) {
auto a = p -> next, b = a -> next;
p -> next = b;
a -> next = b -> next;
b -> next = a;
p = a;
}
return dummy -> next;
}
};
LeetCode 25. K 个一组翻转链表
- 将后k个元素进行翻转操作,共操作k - 1次
- 将后k个元素倒序后的序列的尾部指向后k + 1的元素
- 将p.next指向倒序后的序列的头部
- 更新p指针的位置,即指向倒序后的序列的尾部
/**
* 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* 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; i ++ ) q = q -> next;
//确保有k个节点
if(!q) break;
auto a = p -> next, b = a -> next;
//k个节点内部反正,执行k - 1次
for(int i = 0; i < k - 1; i ++ ) {
auto c = b -> next;
b -> next = a;
a = b, b = c;
}
//c指针指向当前翻转后的k个节点序列尾部
auto c = p -> next;
//c指针的next节点指向原先序列的第k + 1个节点
p -> next = a, c -> next = b;
//p指针指向c指针存储的位置
p = c;
}
return dummy -> next;
}
};
LeetCode 26. 删除排序数组中的重复项
利用指针k记录当前共有几个不重复的元素
初始令 k 为 0,i 从位置 0 开始遍历,若发现 nums[i] 和 nums[i- 1] 不相等,则说明找到新的元素,并且 nums[k ++ ] 赋值为 nums[i],如此循环,直到找到重复元素为止
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int k = 0;
for(int i = 0; i < nums.size(); i ++ )
if(!i || nums[i] != nums[i - 1])
nums[k ++ ] = nums[i];
return k;
}
};
LeetCode 27. 移除元素
思路类比LeetCode 26. 删除排序数组中的重复项
利用指针k记录当前共有几个元素的值不等于val
1.if(nums[i] != val) nums[k ++ ] = nums[i];
2.if(num[i] == val) continue;
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) continue;
nums[k ++ ] = nums[i];
}
return k;
}
};
LeetCode 28. 实现 strStr()
kmp算法
- 创建模式串的 next 数组,为当前失配后,下一个可以比较的位置。
- 在待匹配串上应用 next 数组。
class Solution {
public:
int strStr(string s, string p) {
if(p.empty()) return 0;
int n = s.size(), m = p.size();
s = ' ' + s, p = ' ' + p;
vector<int> next(m + 1);
for(int i = 2, j = 0; i <= m; i ++ ) {
while(j && p[i] != p[j + 1]) j = next[j];
if(p[i] == p[j + 1]) j ++ ;
next[i] = j;
}
for(int i = 1, j = 0; i <= n; i ++ ) {
while(j && s[i] != p[j + 1]) j = next[j];
if(s[i] == p[j + 1]) j ++ ;
if(j == m) return i - m;
}
return -1;
}
};
LeetCode 29. 两数相除
类似于二进制位运算的思想方法
1、先考虑相除后是正数的情况,
x
y
\frac{x}{y}
yx = t,因此 x = t
×
\times
×y,将b,2
×
\times
× b,4
×
\times
×b,8
×
\times
×b,…2n
×
\times
×b的所有小于a的数值存在exp链表中,且exp链表元素大小从小到大排列
2、从exp末端开始枚举,若a >= exp[i]
,则表示 t 包含1 << i
这个数值,将 2i 加入到res中,并且更新a,a -= exp[i]
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;
LL a = abs((LL)x), b = abs((LL)y);
for(LL i = b; i <= a; i = i + i) exp.push_back(i);
LL res = 0;
for(int i = exp.size() - 1; i >= 0; i -- )
if(a >= exp[i]) {
a -= exp[i];
res += 1ll << i;
}
if(is_minus) res = -res;
if(res > INT_MAX || res < INT_MIN) res = INT_MAX;
return res;
}
};
LeetCode 30. 串联所有单词的子串
- 假设 s 的长度为 n,单词的长度为 len,单词的个数为 tot。
- 由于数组中每个单词长度都是相同的,故可以按照单词的长度 len 来将 s 划分为 len 种候选单词序列。举例来看 barfoothefoobarman,根据单词长度为 3,可以得到 3 种不同的划分,分别是 bar foo the foo bar man、arf oot hef oob arm an 和 rfo oth efo oba rma n 三种。
- 对于以上的每一种划分来说,仅需要用 unordered_map 就可以暴力的算出来,能否能由单词列表中的所有单词拼接而成。
- 首先用hash表存储词典中每个单词出现的个数,每个单词长度我们记为len。
- 对于每个位置,我们同样使用一个hash表存储从这个位置开始的所有字符串及其出现次数,然后依次把它后面长度为len的字符串拿出来。
- 如果这个字符串在hash表中没有出现过,那么返回false;否则判断当前字符串出现个数是否大于目标个数,如果大于,返回false
- 如果等于说明找到了一个新的可满足的字符串,更新cnt,如果所有需要的字符串个数都满足了,就记录答案,返回。
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 - 1 <= 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;
}
};