LeetCode 160. Intersection of Two Linked Lists

题目

Write a program to find the node at which the intersection of two singly linked lists begins.

For example, the following two linked lists:

begin to intersect at node c1.

Example 1:

Input: intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
Output: Reference of the node with value = 8
Input Explanation: The intersected node's value is 8 (note that this must not be 0 if the two lists intersect). From the head of A, it reads as [4,1,8,4,5]. From the head of B, it reads as [5,0,1,8,4,5]. There are 2 nodes before the intersected node in A; There are 3 nodes before the intersected node in B.

Example 2:

Input: intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
Output: Reference of the node with value = 2
Input Explanation: The intersected node's value is 2 (note that this must not be 0 if the two lists intersect). From the head of A, it reads as [0,9,1,2,4]. From the head of B, it reads as [3,2,4]. There are 3 nodes before the intersected node in A; There are 1 node before the intersected node in B.

Example 3:

Input: intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
Output: null
Input Explanation: From the head of A, it reads as [2,6,4]. From the head of B, it reads as [1,5]. Since the two lists do not intersect, intersectVal must be 0, while skipA and skipB can be arbitrary values.
Explanation: The two lists do not intersect, so return null.

Notes:

  • If the two linked lists have no intersection at all, return null.
  • The linked lists must retain their original structure after the function returns.
  • You may assume there are no cycles anywhere in the entire linked structure.
  • Your code should preferably run in O(n) time and use only O(1) memory.

这道题的解法非常巧妙,由于它是要求两个链表在尾部的交集,而这两个链表在相交点之前的长度可能并不相同,因此无法直接从前往后比较,需要将两个链表的尾部进行对齐,使指向两个链表的指针最后剩余的节点数相同。要使两个指针剩余的节点数相同,首先比较直观的想法就是先遍历两个链表,求出两个链表的长度,并让指向长的链表的指针先走多出来的节点数,接下来就可以对两个链表同步比较了,但是这样比较耗时,代码写起来也很繁琐。

另外一种精妙的解法就是连接两个链表,相当于构造虚拟链表A'和B',A'=A+B,B'=B+A,最后A'和B'的长度都是一样的,因此可以同步比较。如何构造这两个虚拟链表呢,那就是当一个链表上的指针走完以后,把它接到另一个链表上。

第一种方法代码如下,时间52ms,86.95%,空间17M,16.69%

/*
 * @lc app=leetcode id=160 lang=cpp
 *
 * [160] Intersection of Two Linked Lists
 */
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (!headA || !headB) {
            return NULL;
        }

        ListNode* p1 = headA;
        ListNode* p2 = headB;

        int len1 = 0;
        int len2 = 0;

        while (p1) {
            len1++;
            p1 = p1->next;
        }

        while (p2) {
            len2++;
            p2 = p2->next;
        }

        p1 = headA;
        p2 = headB;

        while (len1 > len2) {
            p1 = p1->next;
            len1--;
        }

        while (len1 < len2) {
            p2 = p2->next;
            len2--;
        }

        while (p1 != p2 && p1 && p2) {
            p1 = p1->next;
            p2 = p2->next;
        }

        return p1;
    }
};

第二种方法代码如下,时间52ms,86.95%,空间16.9M,21.59%

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (!headA || !headB) {
            return NULL;
        }

        ListNode* p1 = headA;
        ListNode* p2 = headB;

        while (p1 != p2) {
            p1 = p1->next;
            p2 = p2->next;

            if (p1 == p2) {
                return p1;
            }
            
            if (!p1) {
                p1 = headB;
            }
            if (!p2) {
                p2 = headA;
            }
            
        }
        
        return p1;
    }
};

这道题一个比较复杂的地方其实是它的返回条件,两种解法的返回条件都需要好好分析一下:

第一种方法在while循环中判断两个指针是否相等,以及任意一个指针是否为NULL(其实说是任一指针为空,其实两个最后都是同步的,不会存在一个为空一个不为空的情况),如果没有相等就继续比较直到走到NULL,相等就直接返回,返回谁都一样。

第二种方法就比较复杂了,由于在while循环中只判断两个指针是否相等,于是需要在循环里额外比较两个指针是否相等,如果相等就直接返回(包括最后集体指向NULL表示不相交),我觉得应该不会跳到最后的return吧?

这道题的discussion里也干货多多,这个里面有很详细的讲解:https://leetcode.com/problems/intersection-of-two-linked-lists/discuss/49789/My-accepted-simple-and-shortest-C%2B%2B-code-with-comments-explaining-the-algorithm.-Any-comments-or-improvements;这个里面有可视化的题解:https://leetcode.com/problems/intersection-of-two-linked-lists/discuss/49785/Java-solution-without-knowing-the-difference-in-len!

还有一个非常奇妙的通过反转链表和异或操作来实现的,真的很厉害能想到这种方法,代码就不写了,放个链接在这里留个思路:https://leetcode.com/problems/intersection-of-two-linked-lists/discuss/49899/C%2B%2B-solution-using-XOR-trick


2022.11.7

就,还是没想到做法,哎。

第一个做法,先求链表长度,让长的链表先走二者的diff,然后两个一起移动,就可以直接对比两个是否相等了。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p1 = headA;
        ListNode p2 = headB;
        int len1 = 1;
        int len2 = 1;
        while (p1.next != null) {
            p1 = p1.next;
            len1++;
        }
        while (p2.next != null) {
            p2 = p2.next;
            len2++;
        }
        p1 = headA;
        p2 = headB;
        int diff = Math.abs(len2 - len1);
        if (len1 > len2) {
            for (int i = 0; i < diff; i++) {
                p1 = p1.next;
            }
        } else {
            for (int i = 0; i < diff; i++) {
                p2 = p2.next;
            }
        }
        while (p1 != null || p2 != null) {
            if (p1 == p2) {
                return p1;
            }
            p1 = p1.next;
            p2 = p2.next;
        }
        return null;
    }
}

第二种做法,当遍历到一个链表结尾的时候,下一个让它接着遍历另一个链表,相当于变相让这俩链表长度相等,就可以直接比较了。刚开始写代码的时候理解有点问题,用p1.next接到了headB上,那确实就相当于把两个链表前后相连造了个环出来,最后就光荣TLE了。其实应该直接判断p1/p2是否为null,并update p1/p2。而且不应该在while里面判断p1 == p2外面return null,毕竟while的循环条件是while (p1 != p2)……fine, I'm so stupid.

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p1 = headA;
        ListNode p2 = headB;
        while (p1 != p2) {
            if (p1 == null) {
                p1 = headB;
            } else {
                p1 = p1.next;
            }

            if (p2 == null) {
                p2 = headA;
            } else {
                p2 = p2.next;
            }
        }
        return p1;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值