单向链表(进阶)

Posted by 令德湖周杰伦 on 12-21,2023

1.1 链表是否有环

思路:
1.快指针一次走2步,慢指针一次走一步
2.如果没有环,遍历到最后,返回null
3.如果有环,必然会在环中相遇,然后快指针回到头,快慢指针一次走一步
4.再次相遇的节点就是入环点

public static ListNode getLoopNode(ListNode head){
        if(head == null){
            return null;
        }
        ListNode slow = head;
        ListNode fast = head;
        while (slow.next!=null && fast.next != null && fast.next.next!=null){
            slow = slow.next;
            fast = fast.next.next;
            //如果有环,2者在环中相遇
            if(slow == fast){
                //快指针回到头.2者一次走一步
                fast = head;
                while(fast!=null && slow!=null){
                    //相遇的节点就是入环点
                    if(fast == slow){
                        return fast;
                    }
                    fast = fast.next;
                    slow = slow.next;
                }
            }
        }
        return null;
    }

2.链表相交

2.1 两个无环链表是否相交

思路:如果两个链变都没有环,那么各自从头开始遍历到最后一个节点,这个节点必然是一个样的

public boolean isIntersectWithNoLoop(ListNode headA, ListNode headB){
        if(headA == null || headB == null){
            return null;
        }
        ListNode a = headA;
        ListNode b = headB;
        while (a.next!=null){
            a = a.next;
        }
        while (b.next!=null){
            b = b.next;
        }
        //证明链表相交
        if(a == b){
	    return true;
	}
	return false;
}

2.2 返回两个无环链表的相交节点

思路:

  1. 如果不相交返回null
  2. 记录链表的长度la,lb,如果相交,长的链表先走|la-lb|步
  3. 然后在一起走,相遇的节点就是相交节点
public static ListNode getIntersectWithNoLoop(ListNode headA, ListNode headB){
        if(headA == null || headB == null){
            return null;
        }
        ListNode a = headA;
        ListNode b = headB;
        int la = 1;
        while (a.next!=null){
            a = a.next;
            la++;
        }
        int lb = 1;
        while (b.next!=null){
            b = b.next;
            lb++;
        }
        //证明链表相交
        if(a == b){
            int diff = Math.abs(la - lb);
            a = headA;
            b = headB;
            if(la > lb){
                //a先提前走
                while(diff>0){
                    a = a.next;
                    diff--;
                }
            }else{
                //b先提前走
                while(diff>0){
                    b = b.next;
                    diff--;
                }
            }
            //开始一起走
            while(a!=null && b!=null){
                if(a == b){
                    return a;
                }
                a = a.next;
                b = b.next;
            }
        }
        return null;
    }

2.3 返回两个有环链表的相交节点

思路:

  1. 先获取2个链表的入环节点
  2. 如果入环节点相同,那么就是相交后再一条链路上入环的,那么根据2.2求相交节点即可
  3. 如果2个入环点不相同, 那么2个入环点一定是在一个环上,且相交节点就是其中一个入环点
public static ListNode getIntersectWithBothLoop(ListNode headA, ListNode loop1, ListNode headB, ListNode loop2){
        if(headA == null || headB == null || loop1 == null || loop2 == null){
            return null;
        }
        //如果入环节点相同,那么走差值结算,返回相交节点
        if(loop1 == loop2){
            int alength = 1;
            ListNode a = headA;
            while (a.next!=loop1){
                a.next = a;
                alength++;
            }
            int blength = 1;
            ListNode b = headB;
            while (b.next!=loop1){
                b.next = b;
                alength++;
            }
            int diff = Math.abs(alength - blength);
            a = headA;
            b = headB;
            if(alength > blength){
                while (diff!=0){
                    a = a.next;
                    diff--;
                }
            }else {
                while (diff!=0){
                    b = b.next;
                    diff--;
                }
            }
            //开始一起走
            while(a!=null && b!=null){
                if(a == b){
                    return a;
                }
                a = a.next;
                b = b.next;
            }
        }else {
	    //如果在环内相遇,那么返回任意一个入环点即可
            ListNode cur = loop1.next;
            while (cur!=loop1){
                if(cur == loop2){
                    return loop1;
                }
                cur = cur.next;
            }
            return null;
        }
        return null;
    }

2.4 返回两个链表的相交节点

思路:综合2.2和2.3

  1. 先获取2个链表的入环点
  2. 如果都为null,走2.2
  3. 如果都不为空走,走2.3
public static ListNode getIntersectNode(ListNode headA, ListNode headB){
        if(headA == null || headB == null){
            return null;
        }
        ListNode loop1 = getLoopNode(headA);
        ListNode loop2 = getLoopNode(headB);

        if(loop1 == null &&  loop2==null){
            return getIntersectWithNoLoop(headA, headB);
        }
        if(loop1 != null && loop2!=null){
            return getIntersectWithBothLoop(headA, loop1, headB, loop2);
        }
        return null;
    }