在本题中, 单链表可能有环, 也可能无环。 给定两个单链表的头节点 head1和head2,这两个链表可能相交, 也可能不相交。
请实现一个函数, 如果两个链表相交, 请返回相交的第一个节点; 如果不相交, 返回null 即可。
要求: 如果链表1的长度为N, 链表2的长度为M, 时间复杂度请达到 O(N+M), 额外空间复杂度请达到O(1)。
问题1:判断链表是否有环,并返回环的第一个结点:
快慢指针,快指针一次走两步,慢指针一次走一步,当快指针重新追上慢指针的时候,快指针指向链表头部,然后变身为慢指针(一次走一步)
当快指针与慢指针再次相遇时,即为环的第一个结点。
问题2:当两个链表都无环时:
问题退化为求两个无环链表的第一个公共结点的问题(根据链表的特殊性,只有一个next指针,只能连接一个下一个结点,所以第一个公共结点之后,只知道链表的结尾都是相同的),剑指offer上出现过剑指offer——两个链表的第一个公共结点
法1)遍历两个链表,得出两个链表的长度len1, len2,然后在较长的链表上先走len1 - len2个结点,然后在一起走,并且比较,相同返回该结点即可
法2)将链表1,2均压栈stack1, stack2,然后依次弹出,直到遇到不同的结点,此时上一个结点就是第一个公共结点,若有一个栈为空,则两个链表不相交
问题3:当两个链表一个有环一个无环时:
可以得出结论就是这两个链表不相交,因为链表的特性决定了如果两个链表相交的话,它们之后会一直相交下去,无环的和有环的链表不能公共的结点,相矛盾。直接返回null即可
问题4:当两个链表均有环时:环入口是loop1,loop2
情况一:两个链表不相交 loop1 != loop2
此时,遍历两个链表,当其中某一个链表再次回到 loop 时,则确定两个链表不相交
node从环的下一个结点开始遍历,当
node1 == loop1 || node2 == loop2时,此时无相交
情况二:两个链表在同一节点相交
此种情况下,两个链表的环入口肯定是相同的 loop1 == loop2
或者是
情况三:两个链表在不同节点相交 loop1 != loop2
此时,从环的下一个结点开始遍历,当node1 == loop1 || node2 == loop2时 即证明相交
public static class Node{ public int val; public Node next = null; public Node(int val){ this.val = val; } } public static Node findFirstNodeFromLoop(Node head){ if(head == null || head.next == null) return null; Node fast = head; Node slow = head; while(fast != null && fast.next != null){ fast = fast.next.next; slow = slow.next; if(slow == fast) break; } if(fast == null || fast.next == null){ return null; } fast = head; while(fast != slow){ fast = fast.next; slow = slow.next; } return fast; } //有环,且入环第一个结点不同 //1.两个有环链表不相交 //2.两个有环链表相交,但是相交结点不同 public static Node findFirstCommonNodeWithLoop(Node loop1, Node loop2){ if(loop1 == null || loop2 == null) return null; Node node1 = loop1.next; Node node2 = loop2.next; while(node1 != loop1 || node2 != loop2){ System.out.println(node1.val + " " + node2.val); if(node1 == loop2 || node2 == loop1) return node1; node1 = node1.next; node2 = node2.next; } return new Node(1000000); } //无环,找第一个公共结点 //有环,且入环第一个结点相同 //正确 public static Node findFirstCommonNodeWithoutLoop(Node head1, Node head2){ if(head1 == null || head2 == null) return null; int len1 = 0; int len2 = 0; Node node1 = head1; Node node2 = head2; while(node1 != null){ node1 = node1.next; len1++; } while(node2 != null){ node2 = node2.next; len2++; } node1 = len1 > len2 ? head1 : head2; node2 = len1 > len2 ? head2 : head1; for(int i = 0; i < Math.abs(len1 - len2); i++){ node1 = node1.next; } while(node1 != null && node2 != null){ if(node1 == node2) return node1; node1 = node1.next; node2 = node2.next; } return null; }
测试函数
/** * head1,head2为两个链表均有环,且第一个相交结点不同 * head3,head4为两个链表均有环,且无相交结点 * head5,head6为两个链表均有环,且第一个相交结点相同 * @param args */ public static void main(String[] args){ Node head1 = new Node(0); Node head2 = new Node(0); Node node1 = head1; Node node2 = head2; node1.next = new Node(1); node1 = node1.next; node1.next = new Node(2); node1 = node1.next; node1.next = new Node(3); node1 = node1.next; node1.next = new Node(4); node1 = node1.next; node1.next = new Node(5); node1 = node1.next; node1.next = new Node(6); node1 = node1.next; node1.next = new Node(7); node1 = node1.next; node1.next = new Node(8); node1 = node1.next; node1.next = new Node(9); node1 = node1.next; node1.next = head1.next.next.next.next; //4为环入口 node2.next = new Node(11); node2 = node2.next; node2.next = new Node(12); node2 = node2.next; node2.next = new Node(13); node2 = node2.next; node2.next = new Node(14); node2 = node2.next; node2.next = new Node(15); node2 = node2.next; node2.next = new Node(16); node2 = node2.next; node2.next = head1.next.next.next.next.next.next;//6为环入口 Node head3 = new Node(0); Node head4 = new Node(0); node1 = head3; node2 = head4; node1.next = new Node(1); node1 = node1.next; node1.next = new Node(2); node1 = node1.next; node1.next = new Node(3); node1 = node1.next; node1.next = new Node(4); node1 = node1.next; node1.next = new Node(5); node1 = node1.next; node1.next = new Node(6); node1 = node1.next; node1.next = new Node(7); node1 = node1.next; node1.next = new Node(8); node1 = node1.next; node1.next = new Node(9); node1 = node1.next; node1.next = head3.next.next.next.next; //4为环入口 node2.next = new Node(11); node2 = node2.next; node2.next = new Node(12); node2 = node2.next; node2.next = new Node(13); node2 = node2.next; node2.next = new Node(14); node2 = node2.next; node2.next = new Node(15); node2 = node2.next; node2.next = new Node(16); node2 = node2.next; node2.next = head4.next.next.next;//14为环入口 Node head5 = new Node(0); Node head6 = new Node(0); node1 = head5; node2 = head6; node1.next = new Node(1); node1 = node1.next; node1.next = new Node(2); node1 = node1.next; node1.next = new Node(3); node1 = node1.next; node1.next = new Node(4); node1 = node1.next; node1.next = new Node(5); node1 = node1.next; node1.next = new Node(6); node1 = node1.next; node1.next = new Node(7); node1 = node1.next; node1.next = new Node(8); node1 = node1.next; node1.next = new Node(9); node1 = node1.next; node1.next = head5.next.next.next.next; //4为环入口 node2.next = new Node(11); node2 = node2.next; node2.next = new Node(12); node2 = node2.next; node2.next = new Node(13); node2 = node2.next; node2.next = new Node(14); node2 = node2.next; node2.next = new Node(15); node2 = node2.next; node2.next = new Node(16); node2 = node2.next; node2.next = head5.next;//4为环入口 1为公共结点 /** * head1,head2为两个链表均有环,且第一个相交结点不同 * head3,head4为两个链表均有环,且无相交结点 * head5,head6为两个链表均有环,且第一个相交结点相同 * */ Node node = null; node1 = head3; node2 = head4; Node loop1 = findFirstNodeFromLoop( head3 ); Node loop2 = findFirstNodeFromLoop(head4); System.out.println(loop1.val); System.out.println(loop2.val); //两个链表均无环 if(loop1 == null && loop2 == null){ node = findFirstCommonNodeWithoutLoop(node1, node2); } else if((loop1 == null && loop2 != null) || (loop1 != null && loop2 == null)){ node = null; } else if(loop1 == loop2){ //两个链表有相交的结点 while(node1.next != loop1){ node1 = node1.next; } System.out.println(node1.val); node1.next = null; node = findFirstCommonNodeWithoutLoop( head5, head6 ); node1.next = loop1; } else { node = findFirstCommonNodeWithLoop(loop1, loop2); //loop1 != loop2 1.两个链表无相交结点 2.两个链表相交结点不同 } String str = node.val == 1000000 ? "无相同结点 " : "有相同结点 "; System.out.println(str + node.val); }