算法练习(10)-求2个(可能有环的)单链表的相交节点

注:题解来自左程云大佬,记录一下
这个问题可以看做是 算法练习(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);
}

  

posted @ 2021-10-23 17:05  菩提树下的杨过  阅读(53)  评论(0编辑  收藏  举报