21. Merge Two Sorted Lists
把两个有序链表合并。
solution:整体思想比较容易,让两个指针分别对应两个链表的开头,如果第一个指针对应的值比较小,那么头指针res的next则为第一个指针,将第一个指针向后移动。依次类推,直到其中一个链表结束。如果另一个链表还没有循环结束,那么把这个链表链在尾部。
注意:corner case 可能一个链表为空的情况。
22. Generate Parentheses
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
For example, given n = 3, a solution set is:
[ "((()))", "(()())", "(())()", "()(())", "()()()" ]
生成对应的括号必须成对匹配。
solution:利用递归。用left表示左括号的剩余数字,用right表示右括号的剩余数字。如果right>left 则返回
如果left = right =0;则把对应的字符串加入vector<string> 否则在每次递归中给字符串加上“(”或者“)”,然后对函数进行递归调用。
代码实现如下:
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> res;
generate(n,n,"",res);
return res;
}
void generate(int left,int right,string out,vector<string>&res)
{
if(left>right) return;
if(left==0&&right==0) {res.push_back(out);return;}
else
{
if(left>0)generate(left-1,right,out+"(",res);
if(right>0)generate(left,right-1,out+")",res);}
}
};
23. Merge k Sorted Lists
是21题的升级版
solution:我们可以借助21题的函数,先将前两个链表合并,然后依次和之后的链表进行合并。不过这样的时间复杂度比较大。
这里就需要用到分治法 Divide and Conquer Approach。简单来说就是不停的对半划分,比如k个链表先划分为合并两个k/2个链表的任务,再不停的往下划分,直到划分成只有一个或两个链表的任务,开始合并。举个例子来说比如合并6个链表,那么按照分治法,我们首先分别合并0和3,1和4,2和5。这样下一次只需合并3个链表,我们再合并1和3,最后和2合并就可以了。代码中的k是通过 (n+1)/2 计算的,这里为啥要加1呢,这是为了当n为奇数的时候,k能始终从后半段开始,比如当n=5时,那么此时k=3,则0和3合并,1和4合并,最中间的2空出来。当n是偶数的时候,加1也不会有影响,比如当n=4时,此时k=2,那么0和2合并,1和3合并,完美解决问题,参见代码如下:
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if (lists.empty()) return NULL;
int n = lists.size();
while (n > 1) {
int k = (n + 1) / 2;
for (int i = 0; i < n / 2; ++i) {
lists[i] = mergeTwoLists(lists[i], lists[i + k]);
}
n = k;
}
return lists[0];
}
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode *dummy = new ListNode(-1), *cur = dummy;
while (l1 && l2) {
if (l1->val < l2->val) {
cur->next = l1;
l1 = l1->next;
} else {
cur->next = l2;
l2 = l2->next;
}
cur = cur->next;
}
if (l1) cur->next = l1;
if (l2) cur->next = l2;
return dummy->next;
}
};
24.Swap Nodes in Pairs
Given a linked list, swap every two adjacent nodes and return its head.
You may not modify the values in the list's nodes, only nodes itself may be changed.
通过只修改指针而不是修改指针的value来交换链表中的顺序
这个也比较容易,但是需要注意的是,在修改链表指针关系的时候,要先建立新的首尾关系,再去删除旧的首尾关系。
或者先把之后要用到的指针(但是会被修改掉) 先保存下来。
25.Reverse Nodes in k-Group
Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.
k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.
Example:
Given this linked list: 1->2->3->4->5
For k = 2, you should return: 2->1->4->3->5
For k = 3, you should return: 3->2->1->4->5
Note:
- Only constant extra memory is allowed.
- You may not alter the values in the list's nodes, only nodes itself may be changed.
这道题其实乍一看比较麻烦,我也在这里卡了很久。。。基本遇到hard题都要卡
这道题让我们以每k个为一组来翻转链表,实际上是把原链表分成若干小段,然后分别对其进行翻转,那么肯定总共需要两个函数,一个是用来分段的,一个是用来翻转的,我们就以题目中给的例子来看,对于给定链表1->2->3->4->5,一般在处理链表问题时,我们大多时候都会在开头再加一个dummy node,因为翻转链表时头结点可能会变化,为了记录当前最新的头结点的位置而引入的dummy node,那么我们加入dummy node后的链表变为-1->1->2->3->4->5,如果k为3的话,我们的目标是将1,2,3翻转一下,那么我们需要一些指针,pre和next分别指向要翻转的链表的前后的位置,然后翻转后pre的位置更新到如下新的位置:
-1->1->2->3->4->5
| | |
pre cur next
-1->3->2->1->4->5
| | |
cur pre next
这里对k个节点进行翻转的方法叫做头插法,以上图为说明
我们要反转-1 到4之间的数字
翻转之后 1为最后一个 所以令last = pre->next
令cur=last->next. cur为第一个要向前插的数字
头插法需要四个语句:
last->next=cur->next; 建立尾部和被插数字后面的联系
cur->next=pre->next 让被插数字指向开头(在上图中就是把3放入-1 和1 之间 需要两步 3指向1 -1 指向3)
pre->next=cur 第二步
cur=last->next 更新被插数字的位置
以此类推,只要cur走过k个节点,那么next就是cur->next,就可以调用翻转函数来进行局部翻转了,注意翻转之后新的cur和pre的位置都不同了,那么翻转之后,cur应该更新为pre->next,而如果不需要翻转的话,cur更新为cur->next,代码如下所示:
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
if (!head || k == 1) return head;
ListNode *dummy = new ListNode(-1), *pre = dummy, *cur = head;
dummy->next = head;
for (int i = 1; cur; ++i) {
if (i % k == 0) {
pre = reverseOneGroup(pre, cur->next);
cur = pre->next;
} else {
cur = cur->next;
}
}
return dummy->next;
}
ListNode* reverseOneGroup(ListNode* pre, ListNode* next) {
ListNode *last = pre->next, *cur = last->next;
while(cur != next) {
last->next = cur->next;
cur->next = pre->next;
pre->next = cur;
cur = last->next;
}
return last;
}
};