每题代码汇总:登录 - Gitee.com
上一章回顾:数据结构:单链表的应用(力扣算法题)第一章-CSDN博客
1.相交链表
理解题意:
思路:
分别遍历两条单链表的长度,两条单链表长度相减,得到长度差的绝对值,让长一些的链表往前遍历长度差个结点,再让两条链表同步遍历,找相同结点。
注意:此处相同是指地址相同而不是值相同。
代码见下:
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* shortlist = headA;
ListNode* longlist = headB;
if(sizeA > sizeB)
{
longlist = headA;
shortlist = headB;
}
//长链表往前遍历
while(gap--)
{
longlist = longlist->next;
}
//两链表一起走
while(shortlist && longlist)
{
if(shortlist == longlist)
{
return shortlist;
}
shortlist = shortlist->next;
longlist = longlist->next;
}
//不相交
return NULL;
}
最终通过测试。
注意:
2.环形链表
理解题意:
思路一:
使用快慢指针,慢指针一次走一步,快指针一次走两步,如果快指针和慢指针指向同一个结点,说明链表带环。
代码见下:
typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) {
ListNode* slow = head;
ListNode* fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
{
return true;
}
}
return false;
}
最终通过测试。
证明一:
证明为什么快指针每次走两步,慢指针每次走一步就可以相遇?
当slow刚入环时,其与fast的距离最远,假设为N,那么当slow走一步,fast走两步时,距离变为N-1,以此类推,最终N == 0。
证明二:
当快指针每次走3步,走4步……还能满足条件吗?
以每次走三步为例,并且需要对N进行分类讨论:
此时可以得到永远不能相遇的条件:N为奇数,C为偶数。那么,此条件正确吗?
此处假设环的周长为C,头结点到slow结点的长度为L。并且此时fast指针已经环绕X周。
依旧沿用上面的条件:3 * 慢指针路程 == 快指针路程
在追逐过程中,快慢指针所走过的路径长度:
fast:L + xC + C - N
slow:L
此时建立方程式:3L == L + xC + C - N
得:2L == (x+1)C - N,此时分两种情况:
情况1:偶数 = 偶数 - 偶数
情况2:偶数 = 奇数 - 奇数
此时可以证明上述得到的结论:N为奇数,C为偶数 不成立,则当快指针每次走3步,走4步……时,两指针依旧会相遇。
思路二:
使用快慢指针,快指针走3,4,5……步
代码如下:
typedef struct ListNode ListNode;
bool hasCycle(struct ListNode* head) {
ListNode *slow, *fast;
slow = fast = head;
while (fast && fast->next) {
slow = slow->next;
int n = 4; // fast每次⾛三,四……步
while (n--) {
if (fast->next)
fast = fast->next;
else
return false;
}
if (slow == fast) {
return true;
}
}
return false;
}
最终测试通过。
总结:虽然已经证明了快指针不论走多少步都可以满足在带环链表中相遇,但是在编写代码的时候 会有额外的步骤引入,涉及到快慢指针的算法题中通常习惯使用慢指针走⼀步快指针走两步的方式。
3.环形链表II
理解题意:
思路:
使用快慢指针,在环里一定会有相遇点,此时:相遇点到入环结点的距离 == 头结点到入环结点的距离。
代码如下:
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)
{
//相遇点:使用curr == slow,不动head,且使curr与slow一样每次走一步
ListNode* curr = head;
while(curr != slow)
{
curr = curr->next;
slow = slow->next;
}
//入环第一个结点
return curr;
}
}
//不带环
return NULL;
}
最终测试通过。
证明:
为什么在带环链表中,快慢指针相遇点到入环结点的距离 == 头结点到入环结点的距离?
此处,E为入环结点,M为相遇点,R为周长,L为头结点到入环结点的距离。
快慢指针在相遇点走的总路程:
slow = L + X
fast = L + X + nR,nR为fast指针的路程。
因为:2 * slow = fast
则:L = nR - X
L = (n - 1)R + R - X ,其中,L为头结点到入环结点的距离,R-X为相遇点到入环结点的距离。此时,(n-1)R可省略,不影响结果。
本章完。