有关链表的一些题目
文章目录
前言
记录一下经常出错或者思路比较巧妙的链表题目。用于自己之后的查看。
一、Leetcode19. 删除链表的倒数第 N 个结点
1.题目描述
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
2.求解思路
- 只遍历一次链表,找出倒数第 n 个节点方法:(假设该链表共有N个节点)
(1)首先,我们先让一个指针 p1 指向链表的头节点 head,然后走n步;
(2)现在的 p1,只要再走 N-n 步,就能走到链表末尾的空指针了
(3)趁这个时候,再用一个指针 p2 指向链表头节点 head,让 p1 和 p2 同时向前走,p1 走到链表末尾的空指针时前进了 N-n 步,p2 也从 head 开始前进了N-n 步,停留在第 N-n+1 个节点上,即恰好停链表的倒数第 n个节点上。 - 但是在本题中,由于需要删除掉倒数第n个节点,因此需要让指针指向倒数第n+1个节点的位置。
- 另外,由于题目中说的倒数第n个节点又可能是头节点,为了统一写法(不单独判断是不是头节点),需要设置虚拟头节点,因此,两个快慢指针应该都从虚拟头节点开始走,其他的不用改变,最终还是会找到倒数第n-1个节点。
注意!返回的时候也应该返回虚拟头节点的下一个节点。
3.代码
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy=new ListNode(0);//要设置一个虚拟头节点,因为有可能链表只有一个节点,被删除的节点为头节点
dummy->next=head;
ListNode* temp=dummy;//必须从虚拟头节点开始走!!(快指针)因为有可能被删除的节点为头节点
for(int i=0;i<n+1;i++){//要删除倒数第n个节点,所以要让慢指针指向倒数第n+1个节点,这样才能做删除操作
temp=temp->next;
}
ListNode* cur=dummy;//必须从虚拟头节点开始走!!(慢指针)因为有可能被删除的节点为头节点
while(temp!=nullptr){
cur=cur->next;
temp=temp->next;
}
ListNode* t=cur->next;
cur->next=t->next;
delete t;
return dummy->next;//返回的时候要返回虚拟头节点的下一个节点,不能直接返回head,因为head有可能被删除了
}
};
二、Leetcode160. 相交链表
1.题目描述
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
2.求解思路
有两种思路:
- 分别计算出两个链表的长度,然后为了将他们的尾部对齐,即为了让两个指针同时遍历它们时能够同时到达尾节点(因为若是两个链表相交,他们最后一个或几个节点一定是一样的),那么需要让指针先遍历那个较长的链表,先走的长度为两链表的长度差值。然后再同时移动两个指针,直到两个指针相等则表示两链表相交,若指针指向了nullptr,则表示不相交。
- 同时遍历两个链表A和B,遍历A的那个指针在遍历完A之后再去遍历B,同理,遍历B的那个指针在遍历完B之后再去遍历A。如果遍历过程中两指针相等则表示两链表相交,若指针指向了nullptr,则表示不相交。(参考labuladong)
注意!最终要判断两个指针是否相等,而不是他们指向的值是否相等。
3.代码
思路1代码如下:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int a=0,b=0;
ListNode* A=headA;
ListNode* B=headB;
while(A!=nullptr){
a++;
A=A->next;
}
while(B!=nullptr){
b++;
B=B->next;
}
if(a>b){
swap(headB,headA);
swap(a,b);
/*ListNode* temp=headA;
headA=headB;
headB=temp;
int n=a;
a=b;
b=n;*/
}
int n=b-a;
while(n){
headB=headB->next;
n--;
}
while(headA!=nullptr){
if(headA==headB) return headA;
else{
headA=headA->next;
headB=headB->next;
}
}
return NULL;
}
};
思路2代码如下:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {//很巧的一个思路
ListNode* p1=headA;
ListNode* p2=headB;
while(p1!=p2){
if(p1==nullptr) p1=headB;
else p1=p1->next;
if(p2==nullptr) p2=headA;
else p2=p2->next;
}
return p1;
}
};
三、Leetcode86. 分隔链表
1.题目描述
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你应当 保留 两个分区中每个节点的初始相对位置。
2.求解思路
这里需要分解让你把原链表一分为二,可以把原链表分成两个小链表,一个链表中的元素大小都小于 x,另一个链表中的元素都大于等于 x,最后再把这两条链表接到一起,就得到了题目想要的结果。(参考labuladong)
创建链表的时候要注意先创建虚拟头节点,返回的时候也要返回虚拟头节点的下一个节点。
3.代码
代码如下:
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode* dummy1=new ListNode(0);
ListNode* dummy2=new ListNode(0);
ListNode* p1=dummy1;
ListNode* p2=dummy2;
ListNode* p=head;
while(p!=nullptr){
if(p->val<x){
p1->next=p;
p1=p1->next;
}
else{
p2->next=p;
p2=p2->next;
}
ListNode* temp=p->next;
p->next=nullptr;
p=temp;
}
ListNode* cur=dummy2->next;
dummy2->next=nullptr;
p1->next=cur;
return dummy1->next;
}
};
总结
以上介绍了几个易错的链表题目,用于自己之后查看。注意虚拟头节点的建立!