判断两个链表是否有交点
问题描述
判断两个单链表是否相交,如果相交,给出相交的第一个点(假设两个链表都不存在环)。相交的链表示意图如下所示。
解题思路
方法一
两个没有环的链表如果是相交于某一结点,如上图所示,这个结点后面都是共有的。所以如果两个链表相交,那么两个链表的尾结点的地址也是一样的。程序实现时分别遍历两个单链表,直到尾结点。判断尾结点地址是否相等即可。时间复杂度为O(L1+L2)。
如何找到第一个相交结点?判断是否相交的时候,记录下两个链表的长度,算出长度差len,接着先让较长的链表遍历len个长度,然后两个链表同时遍历,判断是否相等,如果相等,就是第一个相交的结点。
void Is_2List_Intersect(LinkList L1, LinkList L2) {
if (L1 == NULL || L2 == NULL) {
exit(ERROR);
}
LinkList p = L1;
LinkList q = L2;
int L1_length = 0;
int L2_length = 0;
int len = 0;
while (p->next) {
L1_length ++;
p = p->next;
}
while (q->next) {
L2_length ++;
q = q->next;
}
printf("p: = %d\n", p);
printf("q: = %d\n", q);
printf("L1_length: = %d\n", L1_length);
printf("L2_length: = %d\n", L2_length);
if (p == q) {
printf(" 相交\n");
/*p重新指向短的链表 q指向长链表*/
if (L1_length > L2_length) {
len = L1_length - L2_length;
p = L2->next;
q = L1->next;
}
else {
len = L2_length - L1_length;
p = L1->next;
q = L2->next;
}
while (len) {
q = q->next;
len--;
}
while (p != q) {
p = p->next;
q = q->next;
}
printf("相交的第一个结点是:%d\n", p->data );
}
else {
printf("不相交 \n");
}
}
-
方法一、用HashSet来解决
1 public ListNode EntryNodeOfLoop(ListNode pHead){ 2 HashSet<ListNode> hs = new HashSet<ListNode>(); 3 while(pHead!=null){ 4 if(!hs.add(pHead))//如果包含了,那么这个就是入口结点 5 return pHead; 6 pHead = pHead.next; 7 } 8 return null; 9 }
方法二、计算循环
用两个指针,一个fast指针,每次走两步,一个slow指针,每次走一步,当fast指针与slow指针相遇时,假设fast指针走了2x,那么slow指针走了x,由于有环,那么为了便于理解,分为两种情况
情况一:
1、当fast指针仅仅只比slow指针多走一个环,如图所示
2、第一次相遇的时候,如图
3、这个时候将fast 重新赋值为开头,如图
4、再走两次,则找到了环的入口结点
重新梳理一下步骤,解题思路
a、第一步,找环中相汇点。分别用fast,slow指向链表头部,slow每次走一步,fast每次走二步,直到fast==slow找到在环中的相汇点。
b、第二步,找环的入口。接上步,当fast==slow时,fast所经过节点数为2x,slow所经过节点数为x,设环中有n个节点,fast比slow多走一圈有2x=n+x; n=x;
可以看出slow实际走了一个环的步数,再让fast指向链表头部,slow位置不变。
假设链表开头到环接口的距离是y,如下图所示,那么x-y表示slow指针走过的除链表开头y在环中走过的距离,那么slow再走y步,此时fast结点与slow结点相遇,fast == slow ,x-y+y=x = n,即此时slow指向环的入口。
情况二、当fast比slow 多走n个环
解题思路
a、第一步,找环中相汇点。分别用fast,slow指向链表头部,slow每次走一步,fast每次走二步,直到fast==slow找到在环中的相汇点。
b、第二步,找环的入口。接上步,当fast==slow时,fast所经过节点数为2x,slow所经过节点数为x,设环中有n个节点,fast比slow多走r圈有2x=rn+x; x=rn;(r为圈数,n为一圈的结点数)
可以看出slow实际走了多个环的步数,再让fast指向链表头部,slow位置不变。
假设链表开头到环接口的距离是y,那么x-y表示slow指针走过的除链表开头y在环中走过的距离,那么slow再走y步,此时fast结点与slow结点相遇,fast == slow ,x-y+y=x = rn,即此时slow指向环的入口。
方法二的代码实现
1 public ListNode EntryNodeOfLoop2(ListNode pHead){ 2 ListNode fast = pHead; 3 ListNode slow = pHead; 4 while(fast != null && fast.next != null){ 5 fast = fast.next.next; 6 slow = slow.next; 7 //当快指针 与 慢指针相遇时 8 if(fast == slow){ 9 fast = pHead; 10 //再次相遇 11 while(fast != slow){ 12 fast = fast.next; 13 slow = slow.next; 14 } 15 return fast; 16 } 17 } 18 return null; 19 }