两个单向链表,找出它们的第一个公共结点。链表结点的定义为:
struct ListNode{
int m_nKey;
ListNode* m_pNext;
};
如果两个单向链表有公共的结点,也就是说两个链表从某一结点开始,它们的 m_pNext 都指向同一个结点。但由于是单向链表的结点,每个结点只有一个 m_pNext ,因此从第一个公共结点开始,之后它们所有结点都是重合的,不可能再出现分叉。所以,两个有公共结点而部分重合的链表,像一个 Y ,而不可能像 X 。
如果两个链表有一个公共结点,那么 该公共结点之后的所有结点都是重合的。那么,它们的最后一个结点必然是重合的。因此,我们判断两个链表是不是有重合的部分,只要分别遍历两个链表到最后一个结点。如果两个尾结点是一样的,说明它们用重合;否则两个链表没有公共的结点。
在上面的思路中,顺序遍历两个链表到尾结点的时候,我们不能保证在两个链表上同时到达尾结点。这是因为两个链表不一定长度一样。遍历到相同长度,之后再同步遍历,这个时候我们就能保证同时到达最后一个结点了。由于两个链表从第一个公共结点考试到链表的尾结点,这一部分是重合的。因此,它们肯定也是同时到达第一公共结点的。于是在遍历中,第一个相同的结点就是第一个公共的结点。
我们先要分别遍历两个链表得到它们的长度,并求出两个长度之差。在长的链表上先遍历相同长度之后,再同步遍历两个链表,知道找到相同的结点,或者一直到链表结束。此时,如果第一个链表的长度为 m ,第二个链表的长度为 n ,该方法的时间复杂度为 O(m+n) 。
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
typedef struct ListNode
{
int m_nKey;
ListNode * m_pNext;
}ListNode,*LinkList;
//初始化单链表
int InitListNode(LinkList * L)
{
(*L)=(LinkList)malloc(sizeof(ListNode)); //分配内存空间
if((*L)==NULL)
{
printf("内存分配失败!");
return ERROR;
}
(*L)->m_pNext = NULL;
return OK;
}
//创建单链表
int CreateList(LinkList * L,int n)
{
LinkList P,Q;
Q=(*L);
printf("请输入%d个数字:\n",n);
for(int i=0;i<n;i++)
{
P=(LinkList)malloc(sizeof(ListNode));
scanf("%d",&P->m_nKey);
Q->m_pNext=P;
Q=P;
}
P->m_pNext=NULL;
return OK;
}
//输出链表
void Print(LinkList * L)
{
LinkList P;
P=(*L)->m_pNext;
while(P)
{
printf("%d ",P->m_nKey);
P=P->m_pNext;
}
printf("\n");
}
//获得链表的长度
int getLength(LinkList * L)
{
int length=0;
LinkList P;
P=(*L)->m_pNext;
while(P)
{
length++;
P=P->m_pNext;
}
return length;
}
//找出两个链表的第一个公共结点
int FindFirstNode(LinkList * L1,LinkList * L2)
{
//先比较两个链表的长度
int len1=getLength(L1);
int len2=getLength(L2);
//如果L2的长度大于L1的长度,则交换两条链表,保证L1始终为较长的一条链表
int len; //两条链表长度差
if(len2>len1)
{
len = len2-len1;
LinkList * temp;
temp = L1;
L1 = L2;
L2 = temp;
}
else
{
len = len1-len2;
}
LinkList P = (*L1)->m_pNext; //指向L1链表的指针
for(int i=0;i<len;i++)
{
P = P->m_pNext; //较长的L1链表先遍历len个长度(两链表之差)
}
LinkList Q = (*L2)->m_pNext; //指向L2链表的指针
bool flag = false; //设置标志位(判断是否需要修改公共结点)
bool Tag = false; //设置标志位(判断之前是否找到公共结点)
int sameElem = -1; //公共结点的元素
for(int j=0;j<len2;j++) //两条链表一起遍历
{
if(P->m_nKey==Q->m_nKey) //当前元素相同[第一次元素相同,即找到公共结点的时候需要设置共结点](所以需要判断之前是否找到公共结点)
{
if(!Tag)//如果之前没找到公共结点
{
flag = true; //设置为需要设置公共结点
}
else
{
flag = false; //设置为不需要公共结点(如果之前找到公共结点,就不需要设置公共结点了,因为已经存在)
}
}
else //当前元素不相同
{
flag = false; //设置为需要修改公共结点
Tag = false; //设置为不存在公共结点
}
if(flag)
{
sameElem = P->m_nKey;
Tag = true; //设置为已存在公共结点了。
}
P=P->m_pNext; //L1链表移动
Q=Q->m_pNext; //L2链表移动
}
if(!Tag) //如果不存在公共结点
{
sameElem = -1;
}
return sameElem;
}
int main()
{
int num1,num2; //元素个数
LinkList L1,L2;
InitListNode(&L1); //初始化链表L1
printf("请输入链表L1元素个数:");
scanf("%d",&num1);
CreateList(&L1,num1);
printf("输出该链表L1:");
Print(&L1);
InitListNode(&L2); //初始化链表L2
printf("请输入链表L2元素个数:");
scanf("%d",&num2);
CreateList(&L2,num2);
printf("输出该链表L2:");
Print(&L2);
int sameElem = FindFirstNode(&L1, &L2);
printf("两个链表的第一个公共结点为:%d\n",sameElem);
return 0;
}