leetcode:160. 相交链表

题目来源

160. 相交链表
面试题 02.07. 链表相交
剑指 Offer 52. 两个链表的第一个公共节点

题目描述

leetcode:160. 相交链表_第1张图片

struct ListNode {
    int val;
    ListNode *next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};


class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        
    }
};

题目解析

如果两个单链表有共同的节点,那么从第一个共同节点开始,后面的节点都会重叠,直到链表结束。
因为两个链表中有一个共同节点,则这个节点里的指针域指向的下一个节点地址一样,所以下一个节点也会相交,依次类推。所以,若相交,则两个链表呈“Y”字形。如下图:
leetcode:160. 相交链表_第2张图片

遍历记录链表长度

leetcode:160. 相交链表_第3张图片

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (headA == nullptr || headB == nullptr){  // 只要有一个是空链表,就一定不会相交
            return nullptr;
        }
        
        ListNode *tempA = headA;
        int a1 = 1;
        while (tempA->next != nullptr){
            a1++;
            tempA = tempA->next;
        }
        
        ListNode *tempB = headB;
        int a2 = 1;
        while (tempB->next != nullptr){
            a2++;
            tempB = tempB->next;
        }
        
        
        if(tempB != tempB){
            return nullptr;
        }
        
        int diff = std::abs(a1 - a2);
        if (a1 >= a2){
            tempA = headA;
            tempB = headB;
        }

        if (a2 > a1){
            tempA = headB;
            tempB = headA;
        }

        for (int i = 0; tempA != nullptr && i < diff; i++){
            tempA = tempA->next;
        }

        while (tempA != nullptr  && tempB != nullptr && tempA != tempB){
            tempA = tempA->next;
            tempB = tempB->next;
        }


        return tempA;
    }
};

使用栈

从头遍历两个链表。创建两个栈,第一个栈存储第一个链表的节点,第二个栈存储第二个链表的节点。每遍历一个节点时,就将该节点入栈。

两个链表都入栈结束之后,则通过top判断栈顶的节点是否相等,如果相等则两个链表相交:如果两个链表相交,则从第一个相交节点开始,后面的节点都相交。

如果两链表相交,则循环出栈,直到遇到两个出栈的节点不相同,则这个节点的后一个节点就是第一个相交的节点。

栈问题要:小心栈溢出

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        std::stack<ListNode *> stackA , stackB;
        ListNode *temp;
        
        temp = headA;
        while (temp){
            stackA.push(temp);
            temp = temp->next;
        }

        temp = headB;
        while (temp){
            stackB.push(temp);
            temp = temp->next;
        }


        while (!stackA.empty() && !stackB.empty() && stackA.top() == stackB.top()){
            temp = stackA.top();
            stackA.pop();
            stackB.pop();
        }

        return temp;
    }
};

使用set

还可以使用hash

遍历链表A,将A中节点对应的指针(地址)插入set中;
遍历链表B,将B中节点对应的指针(地址)在set中查找,发现在set中的第一个节点地址,即为两个链表的交点。

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

        std::unordered_set<ListNode *> set;
        ListNode *temp = headA;
        while (temp != nullptr){
            set.insert(temp);
            temp = temp->next;
        }

        temp = headB;
        while (temp){
            if(set.count(temp)){
                return temp;
            }
            temp = temp->next;
        }

        return nullptr;
    }
};

双指针(最优解)

思路是双指针,分别指向两个链表的头节点,循环这个链表,之后再去循环另一个链表。

分为两种情况:

一种是没有交点;
leetcode:160. 相交链表_第4张图片
循环之后就返回 NULL,因为没有交点。

另一种是有交点;
leetcode:160. 相交链表_第5张图片
原理:
leetcode:160. 相交链表_第6张图片
AD + DC + BD = BD + DC + AD

    


class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if(headA == nullptr || headB == nullptr){
	        return nullptr;
	    }
	
	    ListNode * currA = headA;
	    ListNode * currB = headB;
	    while (currA != currB){
	        currA = (currA == NULL) ? headB : currA->next;
	        currB = (currB == NULL) ? headA : currB->next;
	    }
	
	    return currA;
    }
};

参考

类似题目

题目
leetcode:599. 两个列表的最小索引总和 Minimum Index Sum of Two Lists
leetcode:160. 相交链表 Intersection of Two Linked Lists

你可能感兴趣的:(算法与数据结构,链表,leetcode,算法)