给定两个单链表的头节点head1,head2,判断两个链表是否相交,若相交返回相交的第一个节点,若不相交则返回null
由于单链表可能无环,也可能有环,因此这个问题可以总划分为三个方面:
- 判断一个单链表是否有环
- 判断两个无环链表是否相交
- 判断两个有环链表是否相交
一个有环链表和一个无环链表是不可能相交的,因为若相交则无环链表一定时有环的
1. 判断一个单链表是否有环,有环则返回第一个相交的节点loop,不相交则返回null。
(1)第一种方法:用哈希表,依次将遍历的节点存入,若有重复的则有环,相反则无环。但这种方法的空间复杂度高,无法达到题目要求。
(2)第二种方法:快慢指针的方法。
1. 遍历节点时,快指针走两步,慢指针走一步。
2. 若快指针到null,则为无环返回null。
3. 若快指针和慢指针相遇,快指针回到head,且之后一次走一步。
4. 快指针和慢指针相遇的节点,为第一个相交的节点loop,返回该节点
代码如下:
public static Node getLoopNode(Node head){
if(head==null || head.next==null || head.next.next==null){
return null;
}
Node slow = head.next; //慢指针
Node fast = head.next.next; //快指针
while (fast!=slow){
if(fast.next==null ||fast.next.next==null ){
return null; //无环返回null
}
fast = fast.next.next;
slow = slow.next;
}
fast = head; //快指针和慢指针相遇后,快指针回到head
while (fast != slow){
fast = fast.next; //快指针一次走一步
slow = slow.next;
}
return fast; //快指针和慢指针相遇的节点,为第一个相交的节点loop,返回该节点
}
2. 判断两个无环链表是否相交,若相交则返回第一个相交节点,不相交则返回null。
要注意这种情况是不会出现的:
- head1遍历到最后一个结点end1,记录长度为len1。head2遍历到最后一个结点end2,记录长度为len2。
- 若end1!=end2,则两链表不相交,返回null。
- 若相等,则相交。较长的链表先走|len1-len2|步,之后又两链表一起一次走一步,相遇的结点即为相交节点,返回该节点即可
代码如下:
public static Node noLoop(Node head1,Node head2){
if(head1==null || head2==null){
return null;
}
Node cur1 = head1;
Node cur2 = head2;
int n=0;
while (cur1.next != null){
n++;
cur1=cur1.next;
}
while (cur2.next != null){
n--;
cur2=cur2.next;
}
if(cur1!=cur2){
return null;
}
cur1 = n>0 ? head1 : head2;
cur2 = n>0 ? head2 : head1;
n = Math.abs(n);
while (n!=0){
n--;
cur1 = cur1.next;
}
while (cur1!=cur2){
cur1=cur1.next;
cur2=cur2.next;
}
return cur1;
}
3.判断两个有环链表是否相交,若相交则返回第一个节点,若不想交则返回null。
根据1求得的两个链表的第一个入环结点loop1和loop2.
(1)若loop1==loop2,则如下图所示
我们只需要考虑红线中的链表长度即可,与问题二类似。
(2)若loop1!=loop2,则如下图所示,有两种情况,左边是不相交的,右边是相交的
我们可以从loop1出发,遍历链表,在再次回到loop1之前,若未遇到loop2则说明是左边的情况,不相交。若遇到loop2,则说明是右边的情况,相交,返回loop1或loop2都可以。
代码如下:
public Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
Node cur1 = null;
Node cur2 = null;
if (loop1 == loop2) {
cur1 = head1;
cur2 = head2;
int n = 0;
while (cur1.next != loop1) {
n++;
cur1 = cur1.next;
}
while (cur2.next != loop2) {
n--;
cur2 = cur2.next;
}
cur1 = n > 0 ? head1 : head2;
cur2 = n > 0 ? head2 : head1;
n = Math.abs(n);
while (n != 0) {
n--;
cur1 = cur1.next;
}
while (cur1 != cur2) {
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1;
} else {
cur1 = loop1.next;
while (cur1 != loop1) {
if (cur1 == loop2) {
return loop1;
}
cur1 = cur1.next;
}
return null;
}
}
整个题目的主方法代码如下:
public Node getIntersectNode(Node head1, Node head2) {
if (head1 == null || head2 == null) {
return null;
}
Node loop1 = getLoopNode(head1);
Node loop2 = getLoopNode(head2);
if (loop1 == null && loop2 == null) {
return noLoop(head1, head2);
}
if (loop1 != null && loop2 != null) {
return bothLoop(head1, loop1, head2, loop2);
}
return null;
}