给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例 1:
输入:head = [1,2,3,4] 输出:[2,1,4,3]
思考:1.两两交换步骤有三步;
2.下一个循环从哪里开始?上一步交换完了,应该是1节点的next指向4节点,所以下一步循环应该从1结点开始,cur从虚结点开始,往后移动了两次,指向1节点。
解法:需要两个记录指针,一是记录脱离新链表的起始节点(上图中的5节点),二是记录交换是脱离的节点(上图中的3节点);注意一下判断条件,cur->next不等于空,还有cur往后移动两次也不能为空(cur->next->next不为空)。
代码:
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dhead=new ListNode(0);
dhead->next=head;
ListNode* cur=dhead;
while(cur->next!=nullptr&&cur->next->next!=nullptr){
ListNode* temp1=cur->next;//记录需要交换放在后面的节点
ListNode* temp2=cur->next->next->next;//记录等待交换的开始节点
cur->next=cur->next->next;
cur->next->next=temp1;//接上交换放在后面的节点
cur->next->next->next=temp2;//接上后面未处理的节点
cur=cur->next->next;
}
return dhead->next;
}
};
收获:整个交换下来,cur(开始处理指针位置)都没有变过,始终都是用->next和临时指针去处理,这有个好处,就是方便理解下一次循环从哪开始。
19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]
思路:一开始思路就是把链表size求出来,再减去n,一个循环让指针移动size-n就可以了,但是学到新的方法,滑动窗口发现比原来快多了,我不需要计算链表有多长。
解决:利用滑动指针或者说左右指针,先从结果分析,最后左值针应该指向倒数n+1个节点上,右指针应该指向链表尾节点的下一个,right-left刚好等于n+1;
所以分两步:1.创建一个right-left=n+1的窗口;2.同时移动left和right指针,直到right=null。
代码:
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dhead = new ListNode(0);
dhead->next = head;
ListNode* left=dhead;
ListNode* right=dhead;
while(n--&&right!=NULL){
right=right->next;
}
right=right->next;
while(right!=NULL){
right=right->next;
left=left->next;
}
if(left->next!=NULL){
left->next=left->next->next;
}
return dhead->next;
}
};
收获:一开始没有用虚头节点,当n=1时,链表只有一个元素时候出错。
面试题 02.07. 链表相交 - 力扣(LeetCode)
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
图示两个链表在节点 c1
开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
思考:问题简单来看就是两个指针最后有没有指向同一个元素,如果指向就说明有交点,那就有一个前提,这两个指针必须同时移动并且同时开始位置要一致,这就引出下一个问题,如何使两个指针在同一位置开始移动?
解决:最好的办法就是从链表长度入手,n-m就是长链表需要移动的距离,短链表从头结点开始就可以。
代码:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int sizeA=0;
int sizeB=0;
ListNode* A=headA;
ListNode* B=headB;
while(A!=NULL){
sizeA+=1;
A=A->next;
}
while(B!=NULL){
sizeB+=1;
B=B->next;
}
A = headA;
B = headB;
if(sizeA>sizeB){
int n=sizeA-sizeB;
while(n--){
A=A->next;
}
}
else{
int n=sizeB-sizeA;
while(n--){
B=B->next;
}
}
while(A!=NULL&&B!=NULL){
if(A==B){
return A;
}
A=A->next;
B=B->next;
}
return NULL;
}
};
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。
今天难题来了。。。
说实话一开始毫无头绪,先看代码随想录的讲解;自己再捋一遍过程。
两个问题:1.有没有环;2.环的起点在哪(难点)
思路:1.利用快慢指针的追击,快走两步,慢走一步,如果两个指针相遇说明有环;
2.从头结点出发一个指针,从相遇节点也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。也就是在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。(以上这些是推导出来的具体过程不重复,指路代码随想录 (programmercarl.com))。
解决:1.快慢指针开始跑,相遇记录为index2,说明有环,没有返回NULL;2.有环,index1从头节点开始,index1和index2都走一步,相遇的节点就是环的起点(入口)。
代码:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast=head;
ListNode* slow=head;
while(fast!=NULL&&fast->next!=NULL){
fast=fast->next->next;
slow=slow->next;
if(fast==slow){
ListNode* index1=head;
ListNode* index2=fast;
while(index1!=index2){
index1=index1->next;
index2=index2->next;
}
return index1;
}
}
return NULL;
}
};
收获:判断有环应该用快慢指针追击解决,试着解决一下这题141. 环形链表 - 力扣(LeetCode)