CONTENTS
- LeetCode 21. 合并两个有序链表(简单)
- LeetCode 22. 括号生成(中等)
- LeetCode 23. 合并K个升序链表(困难)
- LeetCode 24. 两两交换链表中的节点(中等)
- LeetCode 25. K 个一组翻转链表(困难)
LeetCode 21. 合并两个有序链表(简单)
【题目描述】
将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
【示例1】
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
【示例2】
输入:l1 = [], l2 = []
输出:[]
【示例3】
输入:l1 = [], l2 = [0]
输出:[0]
【提示】
两个链表的节点数目范围是 [0, 50]
−
100
≤
N
o
d
e
.
v
a
l
≤
100
-100\le Node.val\le 100
−100≤Node.val≤100
l1
和 l2
均按非递减顺序排列
【分析】
直接模拟即可,每次取两个链表中较小的结点,接到新链表的后面,如果其中一个链表空了,则直接将另一个链表接到新链表的后面。
【代码】
/**
* 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* list1, ListNode* list2) {
auto dummy = new ListNode(-1), cur = dummy; // 用auto才能并排写
while (list1 && list2)
if (list1->val < list2->val) cur = cur->next = list1, list1 = list1->next;
else cur = cur->next = list2, list2 = list2->next;
if (list1) cur->next = list1;
else if (list2) cur->next = list2;
return dummy->next;
}
};
LeetCode 22. 括号生成(中等)
【题目描述】
数字 n
代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合。
【示例1】
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
【示例2】
输入:n = 1
输出:["()"]
【提示】
1 ≤ n ≤ 8 1\le n\le 8 1≤n≤8
【分析】
本题只有小括号,对于这类问题,判断一个括号序列是否合法有一些很重要的推论:
- 任意前缀中,
(
数量一定大于等于)
数量(最重要); (
和)
的数量相等。
我们可以使用 DFS 搜索方案,对于每个位置,只要当前 (
的数量小于
n
n
n 就可以填入,而填入 )
需要满足当前 )
的数量小于
n
n
n 且小于 (
的数量。
【代码】
class Solution {
public:
vector<string> res;
vector<string> generateParenthesis(int n) {
dfs(n, 0, 0, "");
return res;
}
void dfs(int n, int lc, int rc, string now) // lc和rc分别表示左右括号的数量
{
if (lc == n && rc == n) { res.push_back(now); return; }
if (lc < n) dfs(n, lc + 1, rc, now + '(');
if (rc < n && rc < lc) dfs(n, lc, rc + 1, now + ')');
}
};
LeetCode 23. 合并K个升序链表(困难)
【题目描述】
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
【示例1】
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
【示例2】
输入:lists = []
输出:[]
【示例3】
输入:lists = [[]]
输出:[]
【提示】
k
=
=
l
i
s
t
s
.
l
e
n
g
t
h
k == lists.length
k==lists.length
0
≤
k
≤
1
0
4
0\le k\le 10^4
0≤k≤104
0
≤
l
i
s
t
s
[
i
]
.
l
e
n
g
t
h
≤
500
0\le lists[i].length\le 500
0≤lists[i].length≤500
−
1
0
4
≤
l
i
s
t
s
[
i
]
[
j
]
≤
1
0
4
-10^4\le lists[i][j]\le 10^4
−104≤lists[i][j]≤104
lists[i]
按升序排列
lists[i].length
的总和不超过
1
0
4
10^4
104
【分析】
和第21题差不多,我们每次从这
K
K
K 个链表中找出最小的结点,将其接到新链表的后面,可以使用一个小根堆(优先队列 priority_queue
来维护最小值)。需要注意的是我们需要写一个排序算法,C++ 中优先队列的排序算法不是传入一个函数,而是重载了 ()
的结构体,具体实现方式见代码部分。
【代码】
/**
* 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) {
auto dummy = new ListNode(-1), cur = dummy;
priority_queue<ListNode*, vector<ListNode*>, Cmp> Q; // 传入比较结构体
for (auto list: lists)
if (list) Q.push(list); // 判断是否为空
while (Q.size())
{
auto t = Q.top(); Q.pop();
cur = cur->next = t;
if (t->next) Q.push(t->next);
}
return dummy->next;
}
};
LeetCode 24. 两两交换链表中的节点(中等)
【题目描述】
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
【示例1】
输入:head = [1,2,3,4]
输出:[2,1,4,3]
【示例2】
输入:head = [1,2,3,4]
输出:[2,1,4,3]
【示例3】
输入:head = [1]
输出:[1]
【提示】
链表中节点的数目在范围 [0, 100]
内
0
≤
N
o
d
e
.
v
a
l
≤
100
0\le Node.val\le 100
0≤Node.val≤100
【分析】
我们通过画图可以更加直观地分析:
具体步骤如下:
- 当
P->next
和P->next->next
存在时,将其分别记为A
和B
; P->next = B
;A->next = B->next
;B->next = A
;P = A
。
【代码】
/**
* 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), cur = dummy;
dummy->next = head;
while (cur->next && cur->next->next)
{
auto a = cur->next, b = cur->next->next;
cur->next = b, a->next = b->next, b->next = a, cur = a;
}
return dummy->next;
}
};
LeetCode 25. K 个一组翻转链表(困难)
【题目描述】
给你链表的头节点 head
,每 k
个节点一组进行翻转,请你返回修改后的链表。
k
是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k
的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
【示例1】
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
【示例2】
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]
【提示】
链表中的节点数目为
n
n
n
1
≤
k
≤
n
≤
5000
1\le k\le n\le 5000
1≤k≤n≤5000
0
≤
N
o
d
e
.
v
a
l
≤
1000
0\le Node.val\le 1000
0≤Node.val≤1000
【分析】
和上一题的分析类似,我们先画出示意图:
具体步骤如下:
- 先遍历一次看看
P
后面是否存在K
个结点,若不存在直接返回结果,若存在则记最后一个结点为V
; - 分别将
P->next
和P->next->next
记为A
和B
; P->next = V
;A->next = V->next
;P = A
;- 将
B->next
记为C
; B->next = A
;A = B, B = C
;- 重复6~8共 K − 1 K-1 K−1 次。
【代码】
/**
* 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), cur = dummy;
dummy->next = head;
while (true)
{
auto v = cur;
for (int i = 0; i < k; i++)
if (v->next == nullptr) return dummy->next;
else v = v->next;
auto a = cur->next, b = cur->next->next;
cur->next = v, a->next = v->next, cur = a;
for (int i = 0; i < k - 1; i++)
{
auto c = b->next;
b->next = a, a = b, b = c;
}
}
return dummy->next; // 此行避免报错,不会执行
}
};