判断两个单链表是否相交(有环、无环两种)
题目描述:
给定两个单链表的头节点head1和head2,如何判断两个链表是否相交?相交的话返回true,不想交的话返回false。
给定两个链表的头结点head1和head2。请返回一个bool值代表它们是否相交。
链表中节点的类型设置如下:
class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } }
思路:
1、首先判断是否有环,
- 若两个链表都没有环,则进行无环单链表判断是否相交,进入2;
- 若两个链表一个有环一个无环,则直接判断不相交;
- 若两个链表都有环,则分别得到每个链表的入环节点node1,node2,然后进行有环单链表判断是否相交,进入3;
判断是否有环的方法如下:
1 /** 2 * 判断链表是否有环 3 * 判断方法是设置两个指针最初均指向头结点,然后fast每次走2步,slow每次走1步, 4 * 如果链表没有环,则fast指针肯定先指向表尾的null 5 * 如果有环,则fast和slow肯定会相遇。然后第一次相遇后我们将fast指针重新指向头结点, 6 * 然后fast和slow每次都走一步直到第二次相遇,那么第二次相遇的节点即为入环的节点 7 * @param head 8 * @return 若有,则返回入环节点,否则,返回null 9 */ 10 public ListNode judgeRing(ListNode head){ 11 if(head == null){ 12 return null ; 13 } 14 ListNode fast = head ; 15 ListNode slow = head ; 16 17 while(fast != null && fast.next != null){ 18 fast = fast.next.next ; 19 slow = slow.next ; 20 if(fast == slow){ 21 fast = head ; 22 while(fast != slow){ 23 fast = fast.next ; 24 slow = slow.next ; 25 } 26 return slow ; 27 } 28 } 29 return null ; 30 }
有环时查找入环点的方法的证明过程如下:
当fast与slow相遇时,slow还没走完链表,而fast已经在环内循环了n圈了,假设slow在相遇前走了s步,则fast走了2s步,设环长为r,有2s=s+nr,即s=nr.
由上图可知a+x=s, x+y=r,而我们的目标是找到a的位置。设上图那个拱起的曲线的长度为y,有a+x=s=nr=(n-1)r+r=(n-1)r+y+x,则a=(n-1)r+y. 这个公式告诉我们,从链表头和相遇点分别设一个指针,每次各走一步,这两个指针必定相遇,且相遇的第一个点为环入口点。
2、无环单链表是否相交判断有多种方法:
-
- 方法1:先循环链表1,将每个节点的地址进行hash计算存入哈希表,然后计算链表2的每个节点的地址的hash值,若与hash表中对应位置有值,则相交,否则不相交。
- 方法2:见链表1与2进行首尾相连,判断新链表是否有环,若没有,则不相交,若有环,则是相交的。
- 方法3:先计算两个链表的长度L1、L2,若L1 > L2,则先将链表1移动(L1 - L2)个节点,等到链表1和链表2剩下的长度一样的时候,一起向后移动,依次判断当前链表的节点是否相等,若相等,则相交,若到队尾还没有相等的,则不相交
方法3的代码如下:
1 /** 2 * 判断两个无环链表是否相交 3 * @param head1 4 * @param head2 5 * @return 6 */ 7 public boolean judgeIntersectWithoutRing(ListNode head1,ListNode head2){ 8 int len1 = calLen(head1) ; 9 int len2 = calLen(head2) ; 10 ListNode intsect = null ; 11 if(len1 > len2){ 12 intsect = judge(head1,len1,head2,len2) ; 13 }else{ 14 intsect = judge(head2,len2,head1,len1) ; 15 } 16 //判断相交的节点是否为null,返回结果 17 if(intsect != null){ 18 return true ; 19 }else{ 20 return false ; 21 } 22 } 23 24 /** 25 * 计算链表的长度并返回 26 * @return 27 */ 28 private int calLen(ListNode head){ 29 int len = 0; 30 while(head != null){ 31 len++ ; 32 head = head.next ; 33 } 34 35 return len ; 36 } 37 38 /** 39 * 按长链表、短链表的顺序传入两个链表,然后进行判断 40 * 如果相交,则输出首次相交的节点,否则输出null 41 * @return 42 */ 43 private ListNode judge(ListNode headLong,int lenLong, 44 ListNode headShort,int lenShort){ 45 int gap = lenLong - lenShort ; 46 //将较长的链表移动到与短链表等长的位置 47 while(gap != 0){ 48 headLong = headLong.next ; 49 } 50 //开始判断 51 while(headLong != null && headShort != null){ 52 if(headLong == headShort){ 53 return headShort ; 54 }else{ 55 headLong = headLong.next ; 56 headShort = headShort.next ; 57 } 58 } 59 60 return null ; 61 }
3、有环单链表是否相交的判断方法:先比较两个链表的入环节点是否相等,若想等,则相交,若不想等,则从某个链表的入环节点开始循环一周,判断是否有节点等于另一个链表的入环节点,若等于,则相交,否则不相交。
这个有环链表的判断是在得到两个环的入环节点的基础上进行的,比较简单,就不放代码了。