题目集
1. 分隔链表
笔记:
主体思路: 把原链表分成两个小链表,一个链表中的元素大小都小于
x,另一个链表中的元素都大于等于x,最后再把这两条链表接到一起;
代码:
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode *dummy1 = new ListNode(-1);
ListNode *dummy2 = new ListNode(-1);
ListNode *p1 = dummy1, *p2 = dummy2;
ListNode *p = head;
while(p != NULL){
if(p->val >= x){
p2->next = p;
p2 = p2->next;
}else{
p1->next = p;
p1 = p1->next;
}
ListNode *temp = p->next;
p->next = NULL;
p = temp;
}
p1->next = dummy2->next;
return dummy1->next;
}
};
2. 合并 K 个升序链表
原题链接:23. 合并 K 个升序链表 - 力扣(LeetCode)
笔记:
思路:合并
k个有序链表,需快速得到k个节点中的最小节点,接到结果链表上;-->使用优先队列(Priority Queue),把链表节点放入一个最小堆,就可以每次获得
k个节点中的最小节点;优先队列
定义:
priority_queue<int, vector<int>, greater<int> > >pq;/priority_queue<node> q;特点:自动排序,排序的比较函数可重写;操作与queue相同,有push/pop/top/empyt/size等;
代码:
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.empty()) return nullptr;
ListNode *dummy = new ListNode(-1);
ListNode *p = dummy;
priority_queue<ListNode*, vector<ListNode*>, function<bool(ListNode*,ListNode*)>> pq([](ListNode* a,ListNode* b) {return a->val > b->val ;});
for (auto head : lists){
if(head != nullptr){
pq.push(head);
}
}
while(!pq.empty()){
ListNode *node = pq.top();
pq.pop();
p->next = node;
if( node->next != nullptr){
pq.push(node->next);
}
p = p->next;
}
return dummy->next;
}
};
3. 删除链表的倒数第 N 个结点
原题链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
笔记:
寻找链表倒数第N+1个节点,将倒数第N+1的指针指向倒数第N-1的节点,实现删除链表的倒数第 N 个结点;
只遍历一遍链表,步骤:
- 使用两个指针p1与p2,p1先出发k步,然后p2出发,即p1到第k+1个节点的时候,p2指向第1个节点;
- 随后p1、p2同时同步前进,当p1到达末端,指向null的时候,p2就指向倒数第n个节点。
代码:
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *dummy = new ListNode(-1);
dummy->next = head;
ListNode *x = findfromEnd(dummy, n+1);
x->next = x->next->next;
return dummy->next;
}
ListNode* findfromEnd(ListNode* head, int k){
ListNode *p1 = head;
for(int i=0;i<k;i++){
p1 = p1->next;
}
ListNode *p2 = head;
while(p1!=nullptr){
p1 = p1->next;
p2 = p2->next;
}
return p2;
}
};
4. 链表的中间结点
原题链接:876. 链表的中间结点 - 力扣(LeetCode)
笔记:
同样只遍历一遍链表,创建两个指针
fast和slow,每个每次前进的步数不同,达到一个到末尾,另个以在中点的状态,详细:
- 如果链表长度为单数,当
fast->next = nullptr时,fast指向最后一个,slow指向唯一的中间结点;- 当长度为偶数,当
fast = nullptr时,slow会指向两个中间结点中的第二个;- 如果需要返回的是两个中间节点的第一个,判定条件应为
fast->next->next = nullptr;PS: 对末尾的判断条件,可以根据观察
slow到达终点时,fast的情况来写;
代码:
class Solution {
public:
ListNode* middleNode(ListNode* head) {
ListNode *slow = head;
ListNode *fast = head;
while(fast!= nullptr && fast->next!=nullptr){
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
};
5. 环形链表 II
原题链接:142. 环形链表 II - 力扣(LeetCode)
笔记:
巧在思路,先判定是否有环,若
fast、slow相遇,即有环,则slow回到七点,然后和fast同步前行,相遇的位置则是环的起点(如下图所示);
代码:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *slow = head;
ListNode *fast = head;
while(fast != nullptr && fast->next!=nullptr){
fast = fast->next->next;
slow = slow->next;
if(slow==fast) break;
}
if(fast == nullptr || fast->next==nullptr){
return nullptr;
}
slow = head;
while(slow!=fast){
slow = slow->next;
fast = fast->next;
}
return slow;
}
};
5. 相交链表
笔记:
该题有多种解法:
- 将其中一条链表的尾部与其头部相连,转化成一个带环的链表,即将问题转化成求环形链表的环的起点,(若题干要求最初的两个链表形状不变,在求到答案以后释放即可);
- (统一列表长度)将两个链表拼接,两边同步遍历,当上下结点相同时,则为答案;
- (统一列表长度)分别求出两个链条的长度,让两个指针到达链条尾部的距离相同,然后同步前进,即可求出答案(代码如下)。
代码:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int lenA=0, lenB=0;
for(ListNode *p1=headA; p1!=nullptr;p1=p1->next){
lenA++;
}
for(ListNode *p2=headB; p2!=nullptr;p2=p2->next){
lenB++;
}
ListNode *p1=headA;
ListNode *p2=headB;
if(lenA>lenB){
for(int i=0;i<lenA-lenB;i++)
{
p1 = p1->next;
}
}
else{
for(int i=0;i<lenB-lenA;i++)
{
p2 = p2->next;
}
}
while(p1!=p2){
p1 = p1->next;
p2 = p2->next;
}
return p1;
// 没相遇则为空指针
}
};