1.相交链表
题目链接:相交链表https://leetcode.cn/problems/merge-two-sorted-lists/description/
先看代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* getIntersectionNode(struct ListNode* headA,
struct ListNode* headB) {
if (headA == NULL || headB == NULL)
return NULL;
ListNode *pa, *pb;
pa = headA, pb = headB;
while (pa != pb) {
pa = (pa!=NULL) ? pa->next : headB;
pb = (pb!=NULL) ? pb->next : headA;
}
return pa;
}
思路:
解决这个问题我们可以采用双指针法:
- 先判断两个链表的头节点是否为空,其中一个为空则说明两个链表不可能相交直接返回NULL;
- 创建两个指针pa和pb分别指向headA和headB
- 进入循环,以pa和pb相等作为循环条件,在循环中考虑pa和pb为空的情况,如果pa走到了空,就让pa从headB开始走,pb也是一样,如此一来便能找到相交的节点,如果两个链表没有交点,pa和pb最终会同时指向空,最后返回两个指针的任意一个即可
2.环形链表
题目链接:环形链表https://leetcode.cn/problems/linked-list-cycle/description/
先看代码:
typedef struct ListNode ListNode;
bool hasCycle(struct ListNode* head) {
ListNode* fast = head;
ListNode* slow = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) {
return true;
}
}
return false;
}
思路:
- 快慢指针,先定义两个指针slow和fast,slow每次走一步,fast每次走两步,两个指针必然会在环中相遇
- 推导:进环前需要走N步,环的周长为M,那么slow进环时两者所走步数为slow=N,fast=2N,以链表next指针所指的方向为正方向那么就是slow领先M-N步,每次循环flow会相对于slow前进一步,M-N肯定是正整数,所以两个指针一定会在环中相遇。
- 温馨提示:如果是fast每次循环相对于slow前进的步数大于1就不一定相遇了,大家下去自己推导吧!
3.环形链表2
题目链接:环形链表2https://leetcode.cn/problems/linked-list-cycle-ii/description/
先看代码
typedef struct ListNode ListNode;
struct ListNode* detectCycle(struct ListNode* head) {
ListNode* p1 = head;
ListNode* p2 = head;
while(p1&&p1->next){
p1=p1->next->next;
p2=p2->next;
if(p1==p2){
p1=head;
while(p1!=p2){
p1=p1->next;
p2=p2->next;
}
return p1;
}
}
return NULL;
}
思路:
和上面的快慢指针一样,我们依然使用快慢指针:
- 如果存在环,两个指针会在环内相遇
- 两个指针相遇后,将其中一个指针重新指向头节点,随后每个指针都走一步,相遇处即为环的入口节点
- 推导,已知相遇时p1一定比p2多走一个环的距离,设p2走了T步后与p1相遇,进环前链表节点数为N,环长为M,有以下等式:
(1)T=N+X;(2)2T=N+X+M;(1)-(2)得M=N+X;此时p2已经在环中走了X步,只需再走N步就可到达入环节点,而入环前的节点数恰好时N,故两个指针最后相遇的节点就是入环节点
这道题也可以在两个指针相遇后,将环从相遇处裁开,形成两个相交链表, 按照相交链表的方法来做,这里不再多说啦,大家自己下去实践吧