一. 牛客网 链表的回文结构
https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa
思路
1、偶数情况:

2、奇数情况:

代码实现
class PalindromeList {
public:
//1.找中间节点
ListNode* middleNode(ListNode* head) {
//快慢指针
ListNode* fast, * slow;
fast = head;
slow = head;
while (fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
//2.翻转以中间节点为头的链表
ListNode* reverseList(ListNode* head) {
if (head == NULL) {
return head;
}
ListNode* n1, *n2, *n3;
n1 = NULL;
n2 = head;
n3 = n2->next;
while (n2) {
n2->next = n1;
n1 = n2;
n2 = n3;
if (n3)
n3 = n3->next;
}
return n1;
}
bool chkPalindrome(ListNode* A) {
//1.找中间节点
ListNode* mid = middleNode(A);
//2.翻转以中间节点为头的链表
ListNode* right = reverseList(mid);
//3.遍历原链表和反转后的链表,比较节点的值是否相等
ListNode* left = A;
while (right) {
if (left->val != right->val) {
return false;
}
left = left->next;
right = right->next;
}
return true;
}
};
二. leetcode 160 相交链表
https://leetcode.cn/problems/intersection-of-two-linked-lists/description/
思路
共存在三种相交情况(一定会在末尾相遇):
1、在末尾处相交

2、在起点处相交

3、在中间处相交

不存在以下这种情况:因为 c1 只有1个next指针,只能指向一个节点

代码实现
typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
ListNode* pa=headA;
ListNode* pb=headB;
int sizeA=0,sizeB=0;
while(pa)
{
sizeA++;
pa=pa->next;
}
while(pb)
{
sizeB++;
pb=pb->next;
}
//求长度差
int gap=abs(sizeA-sizeB);//求绝对值
//假设法
ListNode* longList=headA;
ListNode* shortList=headB;
if(sizeA<sizeB)
{
longList=headB;
shortList=headA;
}
while(gap--)
{
longList=longList->next;
}
while(shortList)
{
if(longList==shortList)
{
return shortList;
}
shortList=shortList->next;
longList=longList->next;
}
return NULL;
}
三. 环形链表 |
思路
https://leetcode.cn/problems/linked-list-cycle/description/

slow⼀次走⼀步,fast⼀次走2步,fast先进环,假设slow也走完⼊环前的距离,准备进环,此时fast 和slow之间的距离为N,接下来的追逐过程中,每追击⼀次,他们之间的距离缩小1步
追击过程中fast和slow之间的距离变化:

因此,在带环链表中慢指针走⼀步,快指针走两步最终⼀定会相遇。
思考2:快指针⼀次走3步,走4步,...n步⾏吗?
答案是可以的,有兴趣的同学可以自行推导一下,这里就不再赘述了
代码实现
typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) {
if(head==NULL)
{
return false;
}
//快慢指针
ListNode*slow,*fast;
slow=fast=head;
while(fast!=NULL && fast->next!=NULL)
{
slow=slow->next;
fast=fast->next->next;
//如果快指针与慢指针相遇,那么一定有环
if(slow==fast)
{
return true;
}
}
return false;
}
四. 环形链表 ||
https://leetcode.cn/problems/linked-list-cycle-ii/description/
思路

说明:
H为链表的起始点,E为环入口点,M与判环时候相遇点
设:
环的⻓度为R,H到E的距离为L,E到M的距离为 X ,则:M到E的距离为 R-X
在判环时,快慢指针相遇时所⾛的路径⻓度:
fast: L+X + nR
slow:L+X
注意:
1. 当慢指针进⼊环时,快指针可能已经在环中绕了n圈了,n⾄少为1
因为:快指针先进环⾛到M的位置,,最后⼜在M的位置与慢指针相遇
2.慢指针进环之后,快指针肯定会在慢指针⾛⼀圈之内追上慢指针
因为:慢指针进环后,快慢指针之间的距离最多就是环的⻓度,⽽两个指针在移动时,每次它们⾄今
的距离都缩减⼀步,因此在慢指针移动⼀圈之前快,指针肯定是可以追上慢指针的,⽽快指针速度是慢指针的两倍,因此有如下关系是:
2 * (L+X)=L+X+nR
L+X=nR
L=nR-X
L = (n-1)R+(R-X)
(n为1,2,3,4......,n的大小取决于环的大小,环越小n越⼤)
因为R为环的周长,所以无论前面的系数是多少,最终都是环周长的整数倍
所以: L=R-X
即:⼀个指针从链表起始位置运行,⼀个指针从相遇点位置绕环,每次都走⼀步,两个指针最终会在入口点的位置相遇
代码实现
typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head) {
ListNode* slow=head;
ListNode* fast=head;
while(fast && fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
{
//带环
//头节点到入环节点的距离 == 相遇节点到入环节点的距离
ListNode* pcur=head;//这里就再一次用到了head,所以之前不能用head遍历
while(pcur!=slow)
{
pcur=pcur->next;
slow=slow->next;
}
return pcur;
}
}
return NULL;
}
五. 随机链表的复制
https://leetcode.cn/problems/copy-list-with-random-pointer/description/
思路
1、在原链表基础上拷贝节点并插入到原链表中

2、设置 radom 指针(注:这里未将所有的radom指针画完)

3、断开与原链表的链接(注:这里未将所有的radom指针画完)

代码实现
/**
* Definition for a Node.
* struct Node {
* int val;
* struct Node *next;
* struct Node *random;
* };
*/
typedef struct Node Node;
Node* BuyNode(int x) {
Node* newnode = (Node*)malloc(sizeof(Node));
if (newnode == NULL) {
return NULL; // 处理内存分配失败的情况
}
newnode->val = x;
newnode->next = newnode->random = NULL;
return newnode;
}
//在原链表基础上拷贝节点并插入到原链表中
void AddNode(Node* head)
{
Node* pcur=head;
while(pcur)
{
Node* newnode=BuyNode(pcur->val);
Node* next=pcur->next;
newnode->next=next;
pcur->next=newnode;
pcur=next;
}
}
//设置radom
void setRandom(Node* head)
{
Node* pcur=head;
while(pcur)
{
Node* copy=pcur->next;
if(pcur->random)
copy->random=pcur->random->next;
pcur=copy->next;
}
}
struct Node* copyRandomList(struct Node* head) {
if(head==NULL)
{
return head;
}
//在原链表基础上拷贝节点并插入到原链表中
AddNode(head);
//设置radom
setRandom(head);
//断开链表
Node* pcur=head;
Node* copyHead,* copyTail;
copyHead=copyTail=pcur->next;
while(copyTail->next)
{
pcur=copyTail->next;
copyTail->next=pcur->next;
copyTail=copyTail->next;
}
return copyHead;
}
总结
本篇博客深入解析了经典的链表OJ习题,旨在帮助读者掌握链表操作的核心技巧与解题思路,通过对典型例题的剖析和画图理解,助你巩固数据结构基础。
如果大家在练习中发现新的解题角度,或是有没搞懂的知识点,欢迎在评论区留言讨论。后续我也会持续更新数据结构相关的 OJ 解析,咱们一起在刷题中夯实基础,稳步进阶!