链表OJ习题(2)
一. 牛客网 链表的回文结构
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);//设置radomsetRandom(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 解析,咱们一起在刷题中夯实基础,稳步进阶!