面试算法(三十七)两个链表的第一个公共结点

1、题目:输入两个链表,找出他们的第一个公共结点。

struct ListNode
{
     int  m_nKey;
     ListNode*  m_pNext;
};

解法一:

在第一个链表上顺序遍历每个结点,每遍历到一个结点的时候,在第二个链表上顺序遍历每个结点。如果在第二个链表上有一个结点和第一个链表上的结点一样,说明两个链表在这个结点上重合。若两个链表的长度分别为m和n,则时间复杂度为O(mn)。


解法二:

从链表结点定义看出,这两个链表是单向链表。若两个单向链表有公共结点,那么这两个链表从某一结点开始,他们的m_pNext都指向同一个结点。但由于每个结点只有一个m_pNext,因此从第一个公共结点开始,之后它们所有结点都是重合的,不可能再出现分叉。形状像一个Y。

1)可以发现,如果两个链表有公共结点,那么公共结点出现在两个链表的尾部。这时可以从两个链表的尾部开始比较,但单向链表只能从头部开始遍历,于是我们用栈解决。

2)分别把两个链表的结点放入两个栈里,这样两个链表的尾结点就位于栈的栈顶,这时比较两个栈顶的结点是否相同。如果相同,则把栈顶弹出接着比较下一个栈顶,直到找到最后一个相同的结点。

此法需要两个辅助栈,若链表长度分别为m和n,那么空间复杂度是O(m+n),时间复杂度也是O(m+n)。


解法三:

首先遍历链表得到他们的长度,可以知道哪个比较长,以及长多少。第二次遍历时,在比较长的链表上先走若干步,接着再同时在两个链表上遍历,找到的第一个相同的结点就是他们的第一个公共结点。

此法时间复杂度是O(m+n),但是不需要辅助栈了。

ListNode* FindFirstCommonNode(ListNode *pHead1, ListNode *pHead2)
{
	//得到两个链表的长度
	unsigned int nLength1 = GetListLength(pHead1);
	unsigned int nLength2 = GetListLength(pHead2);

	int nLengthDif = nLength1 - nLength2;

	ListNode* pListHeadLong = pHead1;
	ListNode* pListHeadShort = pHead2;

	if(nLength2 > nLength1)
	{
		pListHeadLong = pHead2;
		pListHeadShort = pHead1;
		nLengthDif = nLength2 - nLength1;
	}

	//先在长链表上走几步,再同时在两个链表上遍历
	for(int i=0; i<nLengthDif; ++i)
		pListHeadLong = pListHeadLong->m_pNext;

	while((pListHeadLong != NULL) && (pListHeadShort != NULL) && (pListHeadLong != pListHeadShort))
	{
		pListHeadLong = pListHeadLong->m_pNext;
		pListHeadShort = pListHeadShort->m_pNext;
	}

	//得到第一个公共结点
	ListNode* pFirstCommonNode = pListHeadLong;
	return pFirstCommonNode;
}

unsigned int GetListLength(ListNode* pHead)
{
	unsigned int nLength = 0;
	ListNode* pNode = pHead;
	while(pNode != NULL)
	{
		++nLength;
		pNode = pNode->m_Next;
	}
	return nLength;
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值