1.返回倒数第k个节点
https://leetcode.cn/problems/kth-node-from-end-of-list-lcci/

要求空间复杂度为O(1)
思路:利用快慢指针,让两个指针拉开k的差距。
如下图,当快指针指向最后一个位置时,慢指针指向的就是我们要找的倒数第k个节点。

cs
typedef struct ListNode ListNode;
int kthToLast(struct ListNode* head, int k) {
ListNode* slow=head,* fast=head;
while(k--)
{
fast=fast->next;
}
while(fast)
{
slow=slow->next;
fast=fast->next;
}
return slow->val;
}
2.链表的回文结构
https://leetcode.cn/problems/palindrome-linked-list-lcci/

思路:
1.找到中间节点
2.将后半段逆置,分别与前面的数依次作比较即可。
如下图,

我们在前面学习过查找中间节点与反转(逆置)链表,还不是很熟悉这两种经典算法的同学可以回顾一下前面的内容:
https://blog.csdn.net/gumidc/article/details/160154798?spm=1001.2014.3001.5501
cs
typedef struct ListNode ListNode;
ListNode* middleNode(ListNode* head) {
ListNode* fast, * slow;
fast = slow = head;
while (fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
ListNode* reverseList(ListNode* head) {
ListNode* n1, * n2, * n3;
//判空
if (head == NULL)
return head;
n1 = NULL, n2 = head, n3 = n2->next;
while (n2)
{
n2->next = n1;
n1 = n2;
n2 = n3;
if (n3)
n3 = n3->next;
}
return n1;
}
bool isPalindrome(ListNode* head) {
ListNode* mid=middleNode(head);
ListNode* rmid=reverseList(mid);
while(rmid&&head)
{
if(head->val!=rmid->val)
return false;
rmid=rmid->next;
head=head->next;
}
return true;
}
3.相交链表
https://leetcode.cn/problems/3u1WK4/

思路:
1.**判断是否相交:只需要判断A与B的最后一个节点是否相同,注意要用地址**进行判断
**2.**返回相交的起始节点:记录两链表的长度,长的先走差距步,然后同时走,出现第一个相同的节点就是交点。

*假设法
**假设A长B短,错误的话修正一下即可。**如下:

cs
typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
int lenA=1,lenB=1;
ListNode* curA=headA,*curB=headB;
while(curA)
{
curA=curA->next;
lenA++;
}
while(curB)
{
curB=curB->next;
lenB++;
}
if(curA!=curB)
{
return NULL;
}
else
{
int gap=abs(lenA-lenB);
ListNode* longlist=headA,*shortlist=headB;
if(lenA<lenB)
{
longlist=headB;
shortlist=headA;
}
while(gap--)
{
longlist=longlist->next;
}
while(longlist!=shortlist)
{
longlist=longlist->next;
shortlist=shortlist->next;
}
return longlist;
}
}
4.环形链表
https://leetcode.cn/problems/linked-list-cycle/

思路:创建一对快慢指针,fast一定先进环,slow进环后,fast就开始追击slow,如果能追上,那么链表就带环。


cs
typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) {
ListNode* fast=head,*slow=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
return true;
}
}
return false;
}
这里我们思考几个问题:
1.为什么一定会相遇,没有可能错过,永远追不上?
如下图:

2.slow一次走一步,fast走三步,四步,N步可以吗?
推理如下:

slow走一步,fast走三步时,有三种情况:

然后再对应讨论C-2或C-1分别为偶数或奇数的情况。
但是,真的会存在永远追不上的情况吗?
注意,根据上述推理分析过程,只有同时存在C是偶数且N是奇数时才会永远追不上
我们画图来进行分析:


所以我们最开始的推理是有问题的,最终正确的结论是:不管fast走几步都一定能追上slow。
5.环形链表II
https://leetcode.cn/problems/linked-list-cycle-ii/

思路:用快慢指针来解决这个问题。
当slow与fast相遇时,我们定义相遇的节点为meet,再让head与meet以相同速度同步向前走,二者首次相遇的节点即为入环的第一个节点。

为什么?我们来推理一下:

最后我们将L的表达式写为(x+1)*C+C-N的形式,其中:
·(x-1)*C表示相遇点绕环x-1圈后,仍会回到相遇点meet
**·**C-N刚好是meet走到入环节点的距离
这说明:head与meet到入环点的距离等价,二者同步前进,首次相遇的位置必然是入环的第一个节点
cs
typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head) {
ListNode* fast=head,*slow=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
ListNode* meet=slow;
while(meet!=head)
{
meet=meet->next;
head=head->next;
}
return meet;
}
}
return NULL;
}
6.随机链表的复制
https://leetcode.cn/problems/copy-list-with-random-pointer/


这道题的难点在于处理random指针,直接复制节点会导致random指向原链表节点,无法实现真正的深拷贝。
思路:
**1.**把拷贝节点插入到原节点的后面,让二者建立关联关系
**2.**处理拷贝节点的random指针
**3.**将拷贝的链表从原链表上摘下来。

第一步:插入拷贝节点
如下图:

注意:要先让copy的next指向原节点的下一个节点,再修改原节点的next,否则会导致后续链表丢失。

第二步:设置random
· 如果random为空,拷贝节点的random也设为空
· 如果random不为空,拷贝节点的random就等于cur->random->next,如下图:


第三步:把拷贝节点取下来尾插到新链表中,然后恢复原链表(不恢复也可以)

代码如下:
cs
typedef struct Node Node;
struct Node* copyRandomList(struct Node* head) {
Node* cur=head;
while(cur)
{
Node* copy=(Node*)malloc(sizeof(Node));
copy->val=cur->val;
copy->next=cur->next;
cur->next=copy;
cur=copy->next;
}
//控制random
cur=head;
while(cur)
{
Node* copy=cur->next;
if(cur->random==NULL)
{
copy->random=NULL;
}
else
{
copy->random=cur->random->next;
}
cur=copy->next;
}
//尾插
cur=head;
Node* copyHead=NULL,*copyTail=NULL;
while(cur)
{
Node* copy=cur->next;
Node* next=copy->next;
if(copyTail==NULL)
{
copyHead=copyTail=copy;
}
else
{
copyTail->next=copy;
copyTail=copy;
}
cur->next=next;
cur=next;
}
return copyHead;
}