一. 两两交换链表中的节点
题目链接/文章讲解/视频讲解: 代码随想录
1.这种while的判断语句就看最后一次进入循环的是什么,这里最后进入循环的是偶数:最后两个节点(->next节点存在,->next->next节点存在),奇数不进入循环了(->next->next为空),剩一个节点不访问了。
while(cur->next != nullptr && cur->next->next != nullptr)
2.弄清楚指针的指向,画个图不容易错,比如cur->next变化了,之后的使用别忘了
cur->next=cur->next->next;
cur->next->next=temp;
代码
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode *prehead=new ListNode(0);
prehead->next=head;
ListNode *cur=prehead;
ListNode *temp;
ListNode *tep;
while(cur->next != nullptr && cur->next->next != nullptr)
//奇数则->next->next为空,只剩一个节点了不再交换 比如只有一个头节点
//满足这两个条件,说明至少有两个节点可以交换
{
tep=cur->next->next->next;
temp=cur->next;
cur->next=cur->next->next;
cur->next->next=temp;
temp->next=tep;
cur=temp;
}
head=prehead->next;
delete prehead;
return head;
}
};
二、删除链表的倒数第N个节点
题目链接/文章讲解/视频讲解: 代码随想录
1.悬挂指针报错问题
本题应该是要返回head=prehead->next;当时没有发现这个问题,一直出现heap-use-after-free on address报错,报错原因是你把prehead的内存释放了,但是head指针仍然指向该地址,导致的报错
head=prehead;
delete prehead;
return head;
2.本题的暴力解法是(虚拟头节点) cur指针指向虚拟头节点,求出数组长度,减去n,然后循环差值次,让指针到达要删除节点的前一个节点,然后进行删除的操作
3.本题可以用双指针进行优化,将求长度和减去n,循环操作合并操作
可以发现fast指针到达末端的时候,和slow到达要删除节点的前一个节点相差n次->next
采用双指针不同出发位置,(相差n次->next)来实现减去n这个操作,即可以让fast指针到达末端时(即求出数组长度),slow指针到达删除节点的前一个节点
代码
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *prehead=new ListNode(0);
prehead->next=head;
ListNode *slow=prehead;
ListNode *fast=prehead;
while(n--)
{
fast=fast->next;
}
while(fast->next)
{
fast=fast->next;
slow=slow->next;
}
ListNode *del=slow->next;
slow->next=slow->next->next;
delete del;
head=prehead->next;
delete prehead;
return head;
}
};
三、链表相交
题目链接/文章讲解: 代码随想录
1.相交的意思是:相交的节点地址相同,其中val和next指针的相同,所以一旦相同后面都相同
2.因为相交后面是相同的,所以先对齐长度(定义首个指针指向的位置直到空地址的节点个数)(以最短的标准)
3.采用求取长度做差,相差几个,指向长的链表的头节点的指针向后移动几位
4.小技巧:运用swap,保持B链表长度大于A,sizeA<sizeB,(swap(sizeA,sizeB))交换指向链表的指针,(swap(curA,curB))或者交换两个链表的地址(swap(headA,headB))
不然会复杂一点,进行两种情况的讨论,这样即保持一种情况,A短B长
if(sizeA>sizeB)
{
swap(sizeA,sizeB);
swap(headA,headB);
}
curA=headA;
curB=headB;
curA=headA;
curB=headB;
if(sizeA>sizeB)
{
swap(sizeA,sizeB);
swap(curA,curB);
}
代码:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
{
ListNode *curA=headA;
ListNode *curB=headB;
int sizeA=0,sizeB=0;
//求取长度
while(curA)
{
curA=curA->next;
sizeA++;
}
while(curB)
{
curB=curB->next;
sizeB++;
}
//计算差值
if(sizeA>sizeB)
{
swap(sizeA,sizeB);
swap(headA,headB);
}
curA=headA;
curB=headB;
int c=sizeB-sizeA;
//移动指针
while(c--)
{
curB=curB->next;
}
//判断是否有交点
while(curB)
{
if(curA==curB)
{
return curB;
}
else
{
curA=curA->next;
curB=curB->next;
}
}
return NULL;
}
};
四、环形链表II
题目链接/文章讲解/视频讲解:代码随想录
1.这题比较复杂的是逻辑的思考
2.采用快慢指针 ,一个快一个慢,如果有环则一定会相遇。
3.两个重要信息:快指针走的路程是慢指针的两倍
快慢指针相遇,即已知相遇的位置
即 (x + y) * 2 = x + y + n (y + z)
当n=1时 x=z;即一个指针从头节点出发(x为它走的路程),会和从相遇的位置出发的指针(z为它走的路程)在环的入口处相遇
当n!=1时,x = n (y + z) - y,按照n=1的逻辑,转化为 x = (n - 1) (y + z) + z(因为已知相遇位置了,所以z是有用的,不知道环的入口,y是用不来的)
n>1说明x比较长,z+y比较短,一个指针从头节点出发(x为它走的路程),一个指针从相遇的位置出发((n - 1) (y + z) + z为它走的路程,先走n-1圈,再走z),最后再环的入口处相遇
代码
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *fast=head;
ListNode *slow=head;
//判断是否有环
while(fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
// 求环的入口
if (slow == fast) {
ListNode* index1 = fast;
ListNode* index2 = head;
while (index1 != index2) {
index1 = index1->next;
index2 = index2->next;
}
return index2; // 返回环的入口
}
}
return NULL;
}
};