22. 括号生成(中等)
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
提示:
1 <= n <= 8
递归
class Solution {
public:
vector<string> ans;
vector<string> generateParenthesis(int n) {
if(n < 0)
return {};
dfs(n, "", 0, 0);
return ans;
}
void dfs(int n, string cur, int l, int r)
{
//l是当前字符串中的左括号个数,r是右括号个数
if(r > l) return;
if(l == n)
{
ans.push_back(cur + string(n - r, ')'));
return;
}
dfs(n, cur + '(', l+1, r);
dfs(n, cur + ')', l, r+1);
}
};
递归
class Solution {
public:
vector<string> res;
vector<string> generateParenthesis(int n) {
helper("", n, n);
return res;
}
void helper(string cur, int left, int right)
{
if(left == 0 && right == 0)
{
res.push_back(cur);
return;
}
if(left > 0)
helper(cur + '(', left-1, right);
if(right > left)
helper(cur + ')', left, right-1);
}
};
24. 两两交换链表中的节点 (中等)
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
使用递归来解决该题,主要就是递归的三部曲:
- 找终止条件:当递归到链表为空或者链表只剩一个元素的时候,终止。
- 找返回值:返回给上一层递归的值应该是已经交换完成后的子链表。
- 单次的过程:因为递归是重复做一样的事情,所以从宏观上考虑,只用考虑某一步是怎么完成的。我们假设待交换的俩节点分别为head和next,next的应该接受上一级返回的子链表(参考第2步)。就相当于是一个含三个节点的链表交换前两个节点,就很简单了。
时间复杂度:O(n),其中 n 是链表的节点数量。需要对每个节点进行更新指针的操作。
空间复杂度:O(n),其中 n 是链表的节点数量。空间复杂度主要取决于递归调用的栈空间。
ListNode* swapPairs(ListNode* head) {
if(head == nullptr || head->next == nullptr)
return head;
ListNode *newhead = head->next;
head->next = swapPairs(newhead->next);
newhead->next = head;
return newhead;
}
解法二:迭代
创建哑结点 dummyHead,令 dummyHead.next = head。令 temp 表示当前到达的节点,初始时 temp = dummyHead。每次需要交换 temp 后面的两个节点。
如果 temp 的后面没有节点或者只有一个节点,则没有更多的节点需要交换,因此结束交换。否则,获得 temp 后面的两个节点 node1 和 node2,通过更新节点的指针关系实现两两交换节点。
具体而言,交换之前的节点关系是 temp -> node1 -> node2,交换之后的节点关系要变成 temp -> node2 -> node1,因此需要进行如下操作。
temp.next = node2
node1.next = node2.next
node2.next = node1
完成上述操作之后,节点关系即变成 temp -> node2 -> node1。再令 temp = node1,对链表中的其余节点进行两两交换,直到全部节点都被两两交换。
两两交换链表中的节点之后,新的链表的头节点是 dummyHead.next,返回新的链表的头节点即可。
-
时间复杂度:O(n),其中 n 是链表的节点数量。需要对每个节点进行更新指针的操作。
-
空间复杂度:O(1)。
ListNode* swapPairs(ListNode* head) {
ListNode *dummy = new ListNode(0);
dummy->next = head;
ListNode *temp = dummy;
while(temp->next != nullptr && temp->next->next != nullptr)
{
ListNode *node1 = temp->next;
ListNode *node2 = node1->next;
temp->next = node2;
node1->next = node2->next;
node2->next = node1;
temp = node1;
}
return dummy->next;
}
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例:
给你这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
说明:
- 你的算法只能使用常数的额外空间。
- 你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
写一个反转链表的函数,然后递归。
时间复杂度O(n),空间复杂度O(1)。
class Solution {
public:
ListNode* reverse(ListNode* head, ListNode* tmp)
{
ListNode *prev = nullptr, *next = nullptr;
while(head != tmp)
{
next = head->next;
head->next = prev;
prev = head;
head = next;
}
return prev;
}
ListNode* reverseKGroup(ListNode* head, int k) {
auto tmp = head;
for(int i = 0; i < k; i++)
{
if(!tmp)
return head;
tmp = tmp->next;
}
auto newhead = reverse(head, tmp);
head->next = reverseKGroup(tmp, k);
return newhead;
}
};