c++ 刷题 输入两个链表,找出它们的第一个公共结点。

题目描述

输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

思路:

初看这题我的第一反应就是这么简单?直接全部遍历找到第一个相同的结点返回就好:

代码:

//自己的
/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        if(!(pHead1 || pHead2))
            return NULL;
        
        ListNode* p1 = pHead1;
        ListNode* p2 = pHead2;
        while(p1)
        {
            while(p2)
            {
                if(p1 == p2)
                    return p1;
                p2 = p2->next;
            }
            p1 = p1->next;
            p2 = pHead2;
        }
        return NULL;
    }
};

这确实也行得通,但是看了看题解,没有一篇文章思路和我的一样,因为我的思路要把第一个链表的元素与第二个链表的结点都进行比较,最坏情况下:时间复杂度为o(m*n)(没有共同结点),是效率非常低的办法。

更高效的解题思路:

1、利用hash表

  • 先把第一个链表遍历一遍建立哈希表(存储的是结点)
  • 再遍历第二个链表找到第一个相同的结点
链接:https://www.nowcoder.com/questionTerminal/6ab1d9a29e88450685099d45c9e31e46?f=discussion
来源:牛客网

//用map做的,时间复杂度O(nlog(n))
classSolution {
public:
    ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) {
        map<ListNode*, int> m;
        ListNode *p = pHead1;
        while(p != NULL) {
            m[p] = 1;
            p = p->next;
        }
        p = pHead2;
        while(p != NULL) {
            if(m[p]) {
                return p;
            }
            p = p->next;
        }
        return NULL;
    }
};

不考虑hash表的构造情况,它的时间复杂度最坏情况下为o(m+n),空间复杂度:o(m)

2、利用栈

  • 把两个链表都放到栈中
  • 两个栈同时出栈,找到第一个不同的元素
  • 该元素的上一个元素就是两个链表的第一个相同结点
链接:https://www.nowcoder.com/questionTerminal/6ab1d9a29e88450685099d45c9e31e46?f=discussion
来源:牛客网

class Solution:
    def FindFirstCommonNode(self, pHead1, pHead2):
        if not pHead1 or not pHead2:
            return None
         
        stack1 = []
        stack2 = []
         
        while pHead1:
            stack1.append(pHead1)
            pHead1 = pHead1.next
             
        while pHead2:
            stack2.append(pHead2)
            pHead2 = pHead2.next
             
        first = None
        while stack1 and stack2:
            top1 = stack1.pop()
            top2 = stack2.pop()
            if top1 is top2:
                first = top1
            else:
                break
        return first

最坏情况下时间复杂度:o(m+n+min(m,n)) 空间复杂度:o(m+n)

3、让两个指针距离表尾相同

链接:https://www.nowcoder.com/questionTerminal/6ab1d9a29e88450685099d45c9e31e46?f=discussion
来源:牛客网

class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) {
        ListNode *p1 = pHead1;
        ListNode *p2 = pHead2;
        while(p1!=p2){
            p1 = (p1==NULL ? pHead2 : p1->next);
            p2 = (p2==NULL ? pHead1 : p2->next);
        }
        return p1;
    }
};

在这里插入图片描述
最坏情况下时间复杂度:o(m+d),d为长链表的未相交元素长度,空间复杂度o(1)

4、找长度差

链接:https://www.nowcoder.com/questionTerminal/6ab1d9a29e88450685099d45c9e31e46?f=discussion
来源:牛客网

/*
找出2个链表的长度,然后让长的先走两个链表的长度差,然后再一起走
(因为2个链表用公共的尾部)
*/
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) {
        int len1 = findListLenth(pHead1);
        int len2 = findListLenth(pHead2);
        if(len1 > len2){
            pHead1 = walkStep(pHead1,len1 - len2);
        }else{
            pHead2 = walkStep(pHead2,len2 - len1);
        }
        while(pHead1 != NULL){
            if(pHead1 == pHead2) return pHead1;
            pHead1 = pHead1->next;
            pHead2 = pHead2->next;
        }
        return NULL;
    }
     
    int findListLenth(ListNode *pHead1){
        if(pHead1 == NULL) return 0;
        int sum = 1;
        while(pHead1 = pHead1->next) sum++;
        return sum;
    }
     
    ListNode* walkStep(ListNode *pHead1, int step){
        while(step--){
            pHead1 = pHead1->next;
        }
        return pHead1;
    }
};

最坏情况下时间复杂度:o(m+n+s+d),s为长度差,d为短链表未相交的部分

小结:

1、总之问题解决办法很多,不应该只想到最简单但是时间复杂度最高的解决办法。
2、解题应多考虑使用hash表、栈、队列等数据结构进行优化,或者使用快慢指针、数学分析等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值