首页 > 代码库 > 两个链表的第一个公共子节点

两个链表的第一个公共子节点

题目:输入两个链表,找出它们的第一个公共子节点。

直观做法:在第一个链表上顺序遍历每个节点,每遍历到一个节点的时候,在第二个链表上顺序遍历每个节点。如果在第二个链表上有一个节点和第一个链表上的节点一样,说明两个链表在这个节点上重合,于是就找到了它们的公共节点。如果第一个链表的长度为 m, 第二个链表的长度为 n,显然,该方法的时间复杂度是 O(mn)。

    分析有公共节点的两个链表的特点:如果两个单向链表有公共节点,那么这两个链表从某个公共的节点开始,它们的 pNext 指针都指向同一个节点。由于是单向链表的节点,每个节点只有一个 pNext,因此,从第一个公共节点开始,之后它们所有的节点都是重合的没有分叉。拓扑形状看起来像一个 Y,而不可能像一个 X。

(一) 如果两个链表有公共节点,那么公共节点出现在两个链表的尾部。如果从两个链表的尾部开始往前比较,最后一个相同的节点就是要找的节点。问题是在单向链表中,只能从头结点开始按顺序遍历,最后才能到达尾节点。最后到达的尾节点却要最先比较,可以想到使用栈的特点来解决这个问题:分别把两个链表的节点放到两个栈里,这样两个链表的尾节点就位于两个栈的栈顶,接下来比较两个栈顶的节点是否相同。如果相同,则把栈顶弹出,接着比较下一个栈顶,直到找到最后一个相同的节点。

    上述思路需要两个辅助栈,如果连表的长度分别为 m 和 n, 那么空间复杂度为 O(m+n)。这种思路的时间复杂度为 O(m+n)。

(二) 当两个链表的长度不相同时,如果从头开始遍历到达尾节点的时间就不一致。解决这一问题还有一个更简单的方法:首先遍历两个链表得到它们的长度,就能知道哪个链表比较长,以及长的链表比短的链表多几个节点。在第二次遍历的时候,在较长的链表上先走若干步,接着在同时在两个链表上遍历,找到的第一个相同的节点就是它们的第一个公共节点。时间复杂度为 O(m+n)。

//两个链表的第一个公共节点
#include<iostream>
using namespace std;

typedef int ElemType;
typedef struct LNode
{
 ElemType data;
 struct LNode *pNext;
}ListNode, *LinkList;

//获取链表长度
int GetListLength(LinkList pHead)
{
 if(pHead == NULL)
  return -1;
 int length = 0;
 ListNode *pNode = pHead->pNext;
 while(pNode != NULL)
 {
  length++;
  pNode = pNode->pNext;
 }
 return length;
}

ListNode* FindFistCommonNode(LinkList pHead1, LinkList pHead2)
{
 int length1 = GetListLength(pHead1);
 int length2 = GetListLength(pHead2);
 int lengthDiff = length1 - length2;
 
 ListNode *pListLong = pHead1->pNext;
 ListNode *pListShort = pHead2->pNext;
 if(length1 < length2)
 {
  pListLong = pHead2->pNext;
  pListShort = pHead1->pNext;
  lengthDiff = length2 - length1;
 }
 //将指向较长的链表的指针后移,使其剩余的节点与较短链表的数目相同
 for(int i = 0; i < lengthDiff; i++)
 {
  pListLong = pListLong->pNext;
 }
 //开始同步移动,找到第一个相同的节点
 while(pListLong != NULL && pListShort != NULL && pListLong != pListShort )
 {
  pListLong = pListLong->pNext;
  pListShort = pListShort->pNext;
 }
 //返回结点指针
 ListNode *firstCommonNode = pListLong;
 return firstCommonNode;
}

void PrintLinkList(ListNode *L)
{
 if(L == NULL)
  return;
 ListNode *pNode = L->pNext;
 while(pNode != NULL)
 {
  cout << pNode->data << " ";
  pNode = pNode->pNext;
 }
 cout << endl;
}

//创建两个含有公共节点的链表(带有头节点)
void InsertLinkList(ListNode *L, int value)
{
 if(L == NULL)
  return;

 ListNode *tempNode = L;
 while(tempNode->pNext != NULL)
  tempNode = tempNode->pNext;
 ListNode* pNode = new ListNode();
 pNode->data = http://www.mamicode.com/value;
 pNode->pNext = tempNode->pNext;
 tempNode->pNext = pNode;
}
void CreateTwoLinkList(ListNode *L1, ListNode *L2)
{
 ListNode *L3 = new ListNode();
 L3->pNext = NULL;
 for(int i = 1; i < 4; i++)
 {
  InsertLinkList(L1, i);
 }
 for(int i = 6; i < 8; i++)
  InsertLinkList(L3, i);

 ListNode *pNode = L1;
 while(pNode->pNext != NULL)
  pNode = pNode->pNext;
 pNode->pNext = L3->pNext;

 for(int i = 4; i < 6; i++)
 {
  InsertLinkList(L2, i);
 }

 pNode = L2;
 while(pNode->pNext != NULL)
  pNode = pNode->pNext;
 pNode->pNext = L3->pNext;

 delete L3;
 L3 = NULL;
}

int main()
{
 ListNode *L1 = new ListNode();
 L1->pNext = NULL;
 ListNode *L2 = new ListNode();
 L2->pNext = NULL;
 CreateTwoLinkList(L1, L2);

 PrintLinkList(L1);
 PrintLinkList(L2);

 ListNode *commonNode = NULL;
 commonNode = FindFistCommonNode(L1, L2);
 cout << commonNode->data << endl;

 system("pause");
 return 0;
}

 

两个链表的第一个公共子节点