单链表相交的第一个节点

题目:给定两个可能有环也可能无环的单链表,头节点head1和head2,请实现一个函数,如果两个链表相交,请返回相交的第一个节点。如果不相交返回null,要求如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度请达到O(1)

public static class Node {
    public int value;
    public Node next;

    public Node(int data) {
        this.value = data;
    }
}

public static 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;
}

// 找到链表第一个入环节点,如果无环,返回null
public static Node getLoopNode(Node head) {
    // 小于2个节点的链表不可能组成环形
    if (head == null || head.next == null || head.next.next == null) {
        return null;
    }
    // 慢节点,一次跳1个节点
    Node slow = head.next; // n1 -> slow
    // 快节点,一次跳2个节点
    Node fast = head.next.next; // n2 -> fast
    // 定理:若链表成环,那么快节点一定会跟慢节点相遇
    while (slow != fast) {
        if (fast.next == null || fast.next.next == null) {
            return null;
        }
        fast = fast.next.next;
        slow = slow.next;
    }
    // 相遇后,快节点回到头部,然后1个1个节点的走,直至和slow相遇
    // 那么这时候相遇的节点就是要找的循环开始节点
    fast = head; // fast -> walk again from head
    while (slow != fast) {
        slow = slow.next;
        fast = fast.next;
    }
    return slow;
}

// 如果两个链表都无环,返回第一个相交节点,如果不想交,返回null
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;
    }
    // n  :  链表1长度减去链表2长度的值
    // n>0说明head1较长
    // 以下步骤是cur1指向较长的链表,cur2指向较短的链表
    cur1 = n > 0 ? head1 : head2; // 谁长,谁的头变成cur1
    cur2 = cur1 == head1 ? head2 : head1; // 谁短,谁的头变成cur2
    n = Math.abs(n);
    while (n != 0) {
        n--;
        cur1 = cur1.next;
    }
    // 这时候,两个链表长度一样,一起走
    // 必定会相遇
    while (cur1 != cur2) {
        cur1 = cur1.next;
        cur2 = cur2.next;
    }
    return cur1;
}

// 两个有环链表,返回第一个相交节点,如果不想交返回null
public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
    Node cur1 = null;
    Node cur2 = null;
    // 这时候两个链表构成的形状是 >-O
    // 这时候可以忽略环,转换为求两个无环链表的相交问题
    if (loop1 == loop2) {
        cur1 = head1;
        cur2 = head2;
        int n = 0;
        // 只遍历到loop1
        while (cur1 != loop1) {
            n++;
            cur1 = cur1.next;
        }
        // 只遍历到loop2
        while (cur2 != loop2) {
            n--;
            cur2 = cur2.next;
        }
        cur1 = n > 0 ? head1 : head2;
        cur2 = cur1 == head1 ? 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;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值