算法練習(10)-求2個(可能有環的)單鏈表的相交節點 算法練習(7)-判斷單鏈表是否有環,以及求環的長度

注:題解來自左程雲大佬,記錄一下
這個問題可以看做是 算法練習(7)-判斷單鏈表是否有環,以及求環的長度 的升級版,分析:對於單鏈表而言,如果2個鏈表能相交,只可能出現下面這幾種情況
鏈表1 類型 鏈表2 類型 相交可能性? 備註 圖例
無環 無環  
無環 有環 × 如果能相交,必然2個都是有環  
有環 有環    或  

 

可以分解成幾個小問題:
1、如何判斷鏈表是有環的?
/**
 * 如果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);
}

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章