链表 相交结点与环问题
基本问题:
1.两个链表中的第一个公共结点
[解题思路]
a.先求得两个链表的长度,得到链表长度差d
b.根据链表长度差,首先让长链表的指针先走d-1步,之后两个指针一起走,发现相同结点时就是公共结点
int len1 = 0, len2 = 0; ListNode p1 = head1, p2 = head2; while (p1 != null) { len1++; p1 = p1.next; } while (p2 != null) { len2++; p2 = p2.next; } ListNode listLong = null, listShort = null; int lenDiff = 0; if(len1 > len2){ listLong = head1; listShort = head2; lenDiff = len1 - len2; } else { listLong = head2; listShort = head1; lenDiff = len2 - len1; } for(int i = 0; i < lenDiff; i++){ listLong = listLong.next; } while(listLong != null && listShort != null && (listLong != listShort)){ listLong = listLong.next; listShort = listShort.next; } return listLong;
2.如何判断一个链表中是否存在环
[解题思路]
和上题类似,维护两个指针,一个指针每次走1步,另一个指针每次走2步,如果快指针追上了慢指针,则链表中存在环
1 public static boolean checkCircle2(ListNode head) { 2 if (head == null) { 3 return false; 4 } 5 6 ListNode fast = head, slow = head; 7 while (fast != null && fast.next != null) { 8 slow = slow.next; 9 fast = fast.next.next; 10 if(slow == fast){ 11 break; 12 } 13 } 14 return !(fast == null || fast.next == null); 15 }
扩展问题:
1.求问题2中环的入口点
如明确知晓链表中存在环,则在环中将链表拆开,则就变成上面的问题1,找到入口结点之后恢复链表
1 public static ListNode findPortal(ListNode head) { 2 if (head == null) { 3 return null; 4 } 5 6 ListNode fast = head, slow = head; 7 ListNode head2 = null, tail = null; 8 while (fast != null && fast.next != null) { 9 slow = slow.next; 10 fast = fast.next.next; 11 // meet in circle, break the circle 12 if (slow == fast) { 13 head2 = fast.next; 14 tail = fast; 15 fast.next = null; 16 break; 17 } 18 } 19 20 ListNode result = findCommonNode(head, head2); 21 tail.next = head2; 22 23 return result; 24 } 25 26 private static ListNode findCommonNode(ListNode head1, ListNode head2) { 27 if (head1 == null || head2 == null) { 28 return null; 29 } 30 int len1 = 0, len2 = 0; 31 ListNode p1 = head1, p2 = head2; 32 while (p1 != null) { 33 len1++; 34 p1 = p1.next; 35 } 36 37 while (p2 != null) { 38 len2++; 39 p2 = p2.next; 40 } 41 42 ListNode listLong = null, listShort = null; 43 int lenDiff = 0; 44 if(len1 > len2){ 45 listLong = head1; 46 listShort = head2; 47 lenDiff = len1 - len2; 48 } else { 49 listLong = head2; 50 listShort = head1; 51 lenDiff = len2 - len1; 52 } 53 54 for(int i = 0; i < lenDiff; i++){ 55 listLong = listLong.next; 56 } 57 58 while(listLong != null && listShort != null && (listLong != listShort)){ 59 listLong = listLong.next; 60 listShort = listShort.next; 61 } 62 63 return listLong; 64 }
这题还有另外的解题思路:http://www.cppblog.com/humanchao/archive/2012/11/12/47357.html
假设两个指针相遇时slow指针走了s步,则fast指针走了2s步(因为慢指针走一步,快指针走两步)
此时fast指针可能绕环走了n圈(n >= 0), 则有2s = s + nr; ==> s = nr;
设链表起点到环的入口点距离为a,环入口点到两指针相遇点距离为x.==>s = a + x;
==>a + x = nr;
==>a + x = (n-1)r + r
==>a + x = (n-1)r + L - a
==>a = (n-1)r + L - x -a;
则我们在发现fast指针和slow指针相遇时,另外使用两个指针,分别指向链表头结点和相遇结点,同时开始走,则他们相遇结点就是
环的入口结点
1 public static ListNode findPortal2(ListNode head) { 2 if (head == null) { 3 return null; 4 } 5 6 ListNode fast = head, slow = head; 7 while(fast != null && fast.next != null){ 8 fast = fast.next.next; 9 slow = slow.next; 10 if(fast == slow){ 11 break; 12 } 13 } 14 15 if(fast == null || fast.next == null){ 16 return null; 17 } 18 19 ListNode p = head; 20 while(p != fast){ 21 p = p.next; 22 fast = fast.next; 23 } 24 return p; 25 }
链表其他问题:
1.求链表倒数第k个结点
2.求链表的中间结点,如链表中结点总数为奇数,返回中间结点;如为偶数,返回中间两个结点的任意一个
问题来源: