剑指 Offer 52. 两个链表的第一个公共节点

Posted 是七喜呀!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指 Offer 52. 两个链表的第一个公共节点相关的知识,希望对你有一定的参考价值。

剑指 Offer 52. 两个链表的第一个公共节点


题目链接: 剑指 Offer 52. 两个链表的第一个公共节点

有关题目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注意:

如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

题解

方法一:暴力法

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    for (struct ListNode* pa = headA; pa != NULL; pa = pa->next)
        for(struct ListNode* pb = headB; pb != NULL; pb = pb->next)
            if (pa == pb) return pa;
    return NULL;
}

方法二:哈希集合

思路:
使用哈希集合,遍历链表A,同时存放链表A中的节点
然后遍历B,判断两者是否右公共节点
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 struct HashTable {
    struct ListNode *key;
    UT_hash_handle hh;
};//哈希表存储链表节点
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct HashTable *hashTable = NULL;
    struct ListNode *temp = headA;//拷贝链表A
    while (temp != NULL)
    {
        struct HashTable *tmp;

        //在hashTable中找temp,返回tmp;
        HASH_FIND(hh, hashTable, &temp, sizeof(struct HashTable *), tmp);
        if (tmp == NULL)
        {
            tmp = malloc(sizeof(struct HashTable));
            tmp->key = temp;

            //把tmp中的key值添加到hashTable中
            HASH_ADD(hh, hashTable, key, sizeof(struct HashTable *), tmp);
            
        }
        temp = temp->next;//指向下一个节点
    }

    //遍历链表B
    temp = headB;//拷贝链表B
    while (temp != NULL)
    {
        struct HashTable *tmp;
        HASH_FIND(hh, hashTable, &temp, sizeof(struct HashTable *), tmp);
        if (tmp != NULL)
            return temp;
        temp = temp->next;
    }
    return NULL;
}

在这里插入图片描述
方法三:栈解法

思路:
从后往前找,将两条链表分别压入两个栈中,然后循环比较两个栈的栈顶元素,同时记录上一位栈顶元素。

当遇到第一个不同的节点时,结束循环,上一位栈顶元素即是答案。
public class Solution {
    public ListNode getIntersectionNode(ListNode a, ListNode b) {
        Deque<ListNode> d1 = new ArrayDeque<>(), d2 = new ArrayDeque<>();
        while (a != null) {
            d1.add(a);
            a = a.next;
        }
        while (b != null) {
            d2.add(b);
            b = b.next;
        }
        ListNode ans = null;
        while (!d1.isEmpty() && !d2.isEmpty() && d1.peekLast() == d2.peekLast()) {
        //peekLast返回最后一个对象或元素
            ans = d1.pollLast();
            //pollLast检索链表的最后一个元素或结尾元素,返回最后一个对象,最后从列表中删除最后一个元素
//列表为空,则它将返回null
            d2.pollLast();
        }
        return ans;
    }
}

在这里插入图片描述

方法四:双指针
代码一:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if (headA == NULL || headB == NULL)
        return NULL;
    struct ListNode *pA = headA, *pB = headB;
    int lenA = 0, lenB = 0;//链表A,B的长度

    //计算A的长度
    while(pA != NULL)
    {
        lenA++;
        pA = pA -> next;
    }

    //计算B的长度
    while(pB != NULL)
    {
        lenB++;
        pB = pB -> next;
    }
    
    //比较两者长度差
    int diff = lenA -lenB;

    //链表A比B长,或相等
    if (diff >= 0)
    {
        pA = headA;

        //因为A比B长diff,所以A先走diff步
        for (int i = 0; i < diff; i++)
            pA = pA -> next;

        pB = headB;

        //然后A,B一起走,相遇时说明找到了第一个相交的结点
        while(pA != NULL)
        {
            if (pA == pB)
                return pA;
            pA = pA -> next;
            pB = pB -> next;
        }
    }
    else
    {
        pB = headB;

        //因为A比B短diff,所以B先走负diff步
        for (int i = 0; i < 0 - diff; i++)
            pB = pB -> next;

        pA = headA;
        while(pA != NULL)
        {
            if (pA == pB)
                return pA;
            pA = pA -> next;
            pB = pB -> next;
        }
    }

    //执行到这里说明A,B不相交,返回NULL
    return NULL;
}

代码一简化版:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if (headA == NULL || headB == NULL)
        return NULL;
    struct ListNode *pA = headA, *pB = headB;
    int lenA = 0, lenB = 0;//链表A,B的长度

    //计算A的长度
    while(pA != NULL && lenA++ >= 0) pA = pA -> next;
    //计算B的长度
    while(pB != NULL && lenB++ >= 0) pB = pB -> next;
    
    //比较两者长度差
    int diff = lenA -lenB;

    pA = headA;
    pB = headB;//重置
    //链表A比B长,或相等
    if (diff >= 0)
    {
        //因为A比B长diff,所以A先走diff步
        for (int i = 0; i < diff; i++)
            pA = pA -> next;        
    }
    else if (diff < 0)
    {
        //因为A比B短diff,所以B先走负diff步
        for (int i = 0; i < 0 - diff; i++)
            pB = pB -> next;
    }
    //然后A,B一起走,相遇时说明找到了第一个相交的结点
        while(pA != NULL)
        {
            if (pA == pB)
                return pA;
            pA = pA -> next;
            pB = pB -> next;
        }
    return NULL;
}

代码二:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if (headA == NULL || headB == NULL)
        return NULL;
    struct ListNode *pA = headA, *pB = headB;//双指针pA,pB
    while (pA != pB)
    {
        pA = pA == NULL ? headB : pA->next;
        pB = pB == NULL ? headA : pB->next;
    }
    return pA;
}

在这里插入图片描述

以上是关于剑指 Offer 52. 两个链表的第一个公共节点的主要内容,如果未能解决你的问题,请参考以下文章

[8月4日]剑指 Offer 52. 两个链表的第一个公共节点

剑指 Offer 52-两个链表的第一个公共节点

剑指 Offer 52. 两个链表的第一个公共节点

剑指 Offer 52. 两个链表的第一个公共节点

算法剑指 Offer 52. 两个链表的第一个公共节点

剑指offer52两个链表的第一个公共节点