解法1:双指针遍历

指针A先遍历链表1,结束了再遍历链表2

指针B先遍历链表2, 结束了再遍历链表1

相遇时:

1.如果有公共节点

假设链表1不在公共节点的部分长度为a1,在公共节点的长度为b,

链表2不在公共节点的部分长度为a2,在公共节点的长度为b

A和B走过了相同的节点数total,A在链表2上走过的节点数为x, B在链表2上走过的节点数为y,

total = a1 + b + x

total = a2 + b + y

很容易得出:x = a2, y = a1

也就是说,他们第二圈会走到第一个相同的节点相遇

2.如果没有相同的节点

那么它会走到链表末尾时等于null而相等

import java.util.*;
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null) {
            return null;
        }
        if (pHead1 == pHead2) {
            return pHead1;
        }
        ListNode pointer1 = pHead1;
        ListNode pointer2 = pHead2;
        while (pointer1 != pointer2) {
            pointer1 = (pointer1 == null) ? pHead2 : pointer1.next;
            pointer2 = (pointer2 == null) ? pHead1 : pointer2.next;
        }
        return pointer1;
    }
}

时间复杂度分析:O(M+N)

空间复杂度分析:O(1)

解法2:计算链表长度 + 双指针

分别计算链表长度,找出他们长度的差值k,长的链表先走k步,然后比较两个指针,相等的话就是第一次相遇

public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null) {
            return null;
        }
        if (pHead1 == pHead2) {
            return pHead1;
        }
        int length1 = getLength(pHead1);
        int length2 = getLength(pHead2);

        int diff = 0;
        ListNode goFirst = null;
        ListNode goLater = null;
        if (length1 <= length2) {
            diff = length2 - length1;
            goFirst = pHead2;
            goLater = pHead1;
        } else {
            diff = length1 - length2;
            goFirst = pHead1;
            goLater = pHead2;
        }

        while (diff != 0) {
            goFirst = goFirst.next;
            diff--;
        }

        while (goFirst != goLater) {
            goFirst = goFirst.next;
            goLater = goLater.next;
        }
        return goFirst;

    }

    private int getLength(ListNode head) {
        ListNode pointer = head;
        int length = 0;
        while (pointer != null) {
            pointer = pointer.next;
            length++;
        }
        return length;
    }
}

复杂度分析

时间复杂度:计算链表长度需要O(m)/O(n), 在移动指针也是O(m)/O(n),最后将两个时间复杂度相加

空间复杂度:O(1)

不考虑时间或者空间复杂度的话也可以考虑以下的解法:

解法3:比较两个链表的倒数第k个节点,从后往前找,直到找到最前的一个相同的节点

复杂度分析

时间复杂度:找倒数第k个节点需要O(n), 找k次倒数第k个节点需要k*O(n)次, 总共是O(n平方)

空间复杂度:O(1)

public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null) {
            return null;
        }
        if (pHead1 == pHead2) {
            return pHead1;
        }
        // phead1 的倒数第k个节点和phead2的倒数第k个节点相同,但是倒数第k+1 个节点不同
        int indexFromEnd = 1;
        ListNode lastKOfPhead1 = findKthNodeFromEnd(pHead1, 1);
        ListNode lastKOfPhead2 = findKthNodeFromEnd(pHead2, 1);
        ListNode lastSameNodeFromEnd = null;
        while (lastKOfPhead1 == lastKOfPhead2) {
            lastSameNodeFromEnd = lastKOfPhead1;
            indexFromEnd++;
            lastKOfPhead1 = findKthNodeFromEnd(pHead1, indexFromEnd);
            lastKOfPhead2 = findKthNodeFromEnd(pHead2, indexFromEnd);
        }
        return lastSameNodeFromEnd;
    }

    public ListNode findKthNodeFromEnd(ListNode head, int k) {
        if (k == 0 || head == null) {
            return head;
        }
        if (head.next == null && k == 1) {
            return head;
        }

        if (head.next == null && k > 1) {
            return null;
        }

        ListNode slow = head;
        ListNode fast = head;
        int count = 1;
        while (count < k && fast.next != null) {
            fast = fast.next;
            count++;
        }
        if (count < k) {
            return null;
        }
        while (fast.next != null) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
}

解法4:空间足够的话可以使用栈

所有的节点入栈,一个一个比较,直到找到不一样的节点为止

复杂度分析

时间复杂度:O(n)

空间复杂度:O(n)

import java.util.*;
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null) {
            return null;
        }
        if (pHead1 == pHead2) {
            return pHead1;
        }
        Stack<ListNode> visited1 = new Stack<ListNode>();
        Stack<ListNode> visited2 = new Stack<ListNode>();
        ListNode pointer1 = pHead1;
        while (pointer1 != null) {
            visited1.push(pointer1);
            pointer1 = pointer1.next;
        }

        ListNode pointer2 = pHead2;
        while (pointer2 != null) {
            visited2.push(pointer2);
            pointer2 = pointer2.next;
        }
        ListNode theLastSameNodeFromEnd = null;
        pointer1 = visited1.pop();
        pointer2 = visited2.pop();
        while (pointer1 == pointer2) {
            theLastSameNodeFromEnd = pointer1;
            if(visited1.empty() || visited2.empty()){
                break;
            }
            pointer1 = visited1.pop();
            pointer2 = visited2.pop();
        }

        return theLastSameNodeFromEnd;
    }
}