题目及测试
package pid160;
/* 相交链表
编写一个程序,找到两个单链表相交的起始节点。
例如,下面的两个链表:
A: a1 → a2
↘
c1 → c2 → c3
↗
B: b1 → b2 → b3
在节点 c1 开始相交。
注意:
如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
致谢:
特别感谢 @stellari 添加此问题并创建所有测试用例。
*/
public class main {
public static void main(String[] args) {
ListNode c=new ListNode(0);
c.next=new ListNode(-1);
LinkList a=new LinkList(1);
a.addLast(2);
a.addLast(3);
a.first.next.next.next=c;
a.printList();
//test(a.first);
LinkList b=new LinkList(5);
b.addLast(6);
b.addLast(4);
b.addLast(2);
b.first.next.next.next.next=c;
b.printList();
test(a.first,b.first);
/*
LinkList c=new LinkList(1);
c.addLast(2);
c.addLast(2);
c.addLast(1);
c.printList();
//test(c.first);
LinkList d=new LinkList(1);
d.addLast(2);
d.addLast(3);
c.printList();
d.printList();
test(c.first,d.first);*/
}
private static void test(ListNode ito,ListNode ito2) {
Solution solution = new Solution();
ListNode rtn;
long begin = System.currentTimeMillis();
System.out.println();
//开始时打印数组
rtn=solution.getIntersectionNode(ito,ito2);//执行程序
long end = System.currentTimeMillis();
System.out.println("rtn=");
rtn.printNodeToEnd();
//System.out.println(":rtn" );
//System.out.print(rtn);
System.out.println();
System.out.println("耗时:" + (end - begin) + "ms");
System.out.println("-------------------");
}
}
解法1(成功,2ms,超快)
首先有一点非常重要,相交节点后的所有节点 (包括相交节点) 都是两个链表共享的。
我们将两个链表的右端对齐,长度较大的链表 的左端的 多出来的节点 肯定不是相交节点,所以我们不考虑左端的这一部分的节点
使用双指针算法,先遍历一遍两个链表的长度,再将两个指针指向两个链表头部,移动长的链表的指针,指向相同距离的位置,再两个指针一起走,如果指针相遇,则得到相同节点
package pid160;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA==null||headB==null){
return null;
}
int lengthA=0;
int lengthB=0;
ListNode now=headA;
while(now!=null){
lengthA++;
now=now.next;
}
now=headB;
while(now!=null){
lengthB++;
now=now.next;
}
ListNode nowA=headA;
ListNode nowB=headB;
if(lengthA>lengthB){
for(int i=0;i<lengthA-lengthB;i++){
nowA=nowA.next;
}
}
if(lengthA<lengthB){
for(int i=0;i<lengthB-lengthA;i++){
nowB=nowB.next;
}
}
ListNode result=null;
while(true){
if(nowA==nowB){
result=nowA;
break;
}
if(nowA==null||nowB==null){
break;
}
nowA=nowA.next;
nowB=nowB.next;
}
return result;
}
}
解法2(别人的)
其实思路和解法1一样,都是双指针,等指针在两个链表的相同位置后,开始比较
我们需要做的事情是,让两个链表从同距离末尾同等距离的位置开始遍历。这个位置只能是较短链表的头结点位置。
为此,我们必须消除两个链表的长度差
指针 pA 指向 A 链表,指针 pB 指向 B 链表,依次往后遍历
如果 pA 到了末尾,则 pA = headB 继续遍历
如果 pB 到了末尾,则 pB = headA 继续遍历
比较长的链表指针指向较短链表head时,长度差就消除了
如此,只需要将最短链表遍历两次即可找到位置
即假设有长度A和B,A长,
双指针从A跑完,pA跑了长度A,到B的headB
pB从B跑,B跑完跑了B,再到A的headA,跑了A-B,
此时pA在B的headB,pB在A的A-B处,是相同的位置。
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) return null;
ListNode pA = headA, pB = headB;
while (pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}