鏈表1 類型 | 鏈表2 類型 | 相交可能性? | 備註 | 圖例 |
無環 | 無環 | √ | ||
無環 | 有環 | × | 如果能相交,必然2個都是有環 | |
有環 | 有環 | √ | 或 |
/** * 如果1個單鏈表有環,返回入環節點 * * @param head * @return 如果有環, 則返回入環節點,否則返回null */ public static LinkNode getLoopEntrance(LinkNode head) { if (head == null || head.next == null) { return null; } LinkNode slow = head; LinkNode fast = head; boolean isLoop = false; //先跑一圈, 判斷是否有環 while (fast.next != null && fast.next.next != null) { slow = slow.next; fast = fast.next.next; if (slow.equals(fast)) { isLoop = true; break; } } if (!isLoop) { return null; } //快指針重回頭部,再跟慢指針齊步向前走,fast與slow必然相交於入環點(數學上可證明,證明過程略) fast = head; while (fast.next != null) { if (slow.equals(fast)) { return slow; } slow = slow.next; fast = fast.next; } return null; }
2、如何判斷2個無環鏈表,是否相交?
/** * 返回2個無環鏈表的相交節點 * * @param head1 鏈表1的頭節點 * @param head2 鏈表2的頭節點 * @return 如果相交,則返回相交節點,否則返回null */ public static LinkNode getNoLoopCrossNode(LinkNode head1, LinkNode head2) { //思路:先測量各自的長度, 然後找出長度差值 //第二輪遍歷時, 讓長度大的鏈表先走差值步,再2個鏈表齊步走, 如果有相交,必然在交叉點相遇 if (head1 == null || head2 == null) { return null; } int size1 = 0, size2 = 0; LinkNode h1 = head1, h2 = head2; while (h1 != null) { size1 += 1; h1 = h1.next; } while (h2 != null) { size2 += 1; h2 = h2.next; } int diff = Math.abs(size1 - size2); //讓h1指向長的鏈表頭 h1 = size1 >= size2 ? head1 : head2; //讓h2指向短的鏈表頭 h2 = h1.equals(head2) ? head1 : head2; //長鏈表先走差值步 for (int i = 0; i < diff; i++) { h1 = h1.next; } //2個鏈表再齊步走 while (h1 != null && h2 != null) { if (h1.equals(h2)) { return h1; } h1 = h1.next; h2 = h2.next; } return null; }
3、如何判斷2個有環鏈表,是否相交?
/** * 判斷2個有環單鏈表是否相交 * * @param entrance1 鏈表1的入環節點 * @param entrance2 鏈表2的入環節點 * @return 如果相交, 返回相交點 */ public static LinkNode getLoopCrossNode(LinkNode entrance1, LinkNode entrance2) { if (entrance1 == null || entrance2 == null) { return null; } //2個入環節點相同, 必然相交 if (entrance1.equals(entrance2)) { return entrance1; } //從入環節點a出發, 一直向前走, 直到再次遇到自己前, 如果路上遇到另1個入環節點b,則a,b肯定在一個環上 LinkNode n1 = entrance1; while (entrance1 != null) { if (entrance1.equals(entrance2)) { return entrance1; } entrance1 = entrance1.next; if (n1.equals(entrance1)) { break; } } return null; }
/** * 如果2個(可能有環的)鏈表相交,返回相交點 * @param h1 鏈表1的頭節點 * @param h2 鏈表2的頭節點 * @return 如果相交,返回相交節點 */ public static LinkNode getCrossNode(LinkNode h1, LinkNode h2) { LinkNode entrance1 = getLoopEntrance(h1); LinkNode entrance2 = getLoopEntrance(h2); if (entrance1 == null && entrance2 == null) { //都是無環鏈表 return getNoLoopCrossNode(h1, h2); } //至少1個是有環鏈表 return getLoopCrossNode(entrance1, entrance2); }