问题描述:
两个单链表有公共结点,也就是两个链表从某一结点开始,它们的指针域都指向同一个结点。由于每个单链表结点只有一个next域,因此从第一个公共结点开始,之后所有的结点都是重合的,不可能再出现分叉,如图所示:
一、实现方法
方法一:
在两个单链表中分别顺序遍历每个链表中的所有结点,如果有相同的结点则找到了公共的结点,但该算法的时间复杂度为O(len1*len2)。
方法二:
如果两个链表有公共结点,则该公共结点之后的所有结点都是重合的,即它们的最后一个结点必然是重合的。因此,我们判断两个链表是不是有重合的部分,只要分别遍历两个链表到最后一个结点。如果两个尾结点是一样的,说明它们有公共结点,否则两个链表没有公共的结点。但在顺序遍历两个链表到尾结点的时,并不能保证在两个链表同时到达尾结点,因为两个链表的长度有可能是不一样。因此假设一个链表比另一个长n个结点,先在长的链表上遍历n个结点,之后再两个链表同步遍历,此时就能保证同时到达最后一个结点。由于两个链表从第一个公共结点开始到链表的尾结点,这一部分是重合的。因此,它们肯定也是同时到达第一公共结点的,于是在遍历中,第一个相同的结点就是第一个公共的结点。
因此在这样的思路中,先要分别遍历两个链表得到它们的长度,并求出两个长度之差。在长的链表上先遍历长度之差个结点之后,再同步遍历两个链表,直到找到第一个相同的结点就结束。该方法的时间复杂度为O(len1+len2)。
二、代码实现
1.链表结构体
代码如下(示例):
typedef struct node
{
int data;
struct node* next;
}SingleList;
2.初始化链表
代码如下(示例):
//初始化A链表
SingleList* a_init_SingleList()
{
SingleList* a_list = malloc(sizeof(SingleList));
if (a_list == NULL)
return NULL;
a_list->data = 0;
a_list->next = NULL;
return a_list;
}
//初始化B链表
SingleList* b_init_SingleList()
{
SingleList* b_list = malloc(sizeof(SingleList));
if (b_list == NULL)
return NULL;
b_list->data = 0;
b_list->next = NULL;
return b_list;
}
//初始化C链表
SingleList* c_init_SingleList()
{
SingleList* c_list = malloc(sizeof(SingleList));
if (c_list == NULL)
return NULL;
c_list->data = 0;
c_list->next = NULL;
return c_list;
}
3.尾插法创建单链表
代码如下(示例):
//尾插法创建单链表
SingleList* create_SingleListByTail(SingleList* list)
{
SingleList* pMove, * pCreate, * head;
int length, data;
head = (SingleList*)malloc(sizeof(SingleList));
head = list;
if (head == NULL)
{
return NULL;
}
else
{
head->next = NULL;
}
pMove = head;
printf("请输入创建链表的长度:");
scanf_s("%d", &length);
for (int i = 0; i < length; i++)
{
scanf_s("%d", &data);
pCreate = (SingleList*)malloc(sizeof(SingleList));
if (pCreate == NULL)
{
return NULL;
}
pCreate->data = data;
pCreate->next = NULL;
//插入操作
pMove->next = pCreate;
pMove = pCreate;
}
return list;
}
4.连接两个单链表
代码如下(示例):
//连接两个链表
SingleList* link(SingleList* list1, SingleList* list2)
{
SingleList* pmove, * qmove;
pmove = list1;
qmove = list2;
while (pmove->next)
{
pmove = pmove->next;
}
pmove->next = qmove->next; //更改list1的指针域,让其指向list2完成连接
return list1;
}
5.求链表的长度
代码如下(示例):
//求链表的长度
int foreach_SingleList(SingleList* list)
{
SingleList* pMove;
int num = 0;
pMove = list->next;
while (pMove != NULL)
{
num++;
pMove = pMove->next;
}
return num;
}
6.寻找两个链表中的公共结点
代码如下(示例):
//寻找两个链表中的公共结点
void searchCommonNodeFromSingleList(SingleList* list1, SingleList* list2)
{
SingleList* pMove, * qMove;
int len1, len2, dis;
len1 = foreach_SingleList(list1);
len2 = foreach_SingleList(list2);
if (len1 > len2) //链表1的长度大于链表2的长度
{
pMove = list1->next; //指向长表
qMove = list2->next; //指向短表
dis = len1 - len2; //两表长度差
}
else
{
pMove = list2->next;
qMove = list1->next;
dis = len2 - len1;
}
while (dis--) //长表先遍历到第dis个结点后再和短表同步遍历
{
pMove = pMove->next;
}
while (pMove != NULL) //寻找公共结点
{
if (pMove == qMove) //找到第一个相同结点
{
break;
}
else
{
pMove = pMove->next;
qMove = qMove->next;
}
}
printf("两个链表的公共结点为:\n");
while (pMove != NULL)
{
printf("%d ", pMove->data);
pMove = pMove->next;
}
printf("\n");
}
7.输出链表中的内容
代码如下(示例):
//打印
void printf_SingleList(SingleList* list)
{
SingleList* pMove;
pMove = list->next;
while (pMove != NULL)
{
printf("%d ", pMove->data);
pMove = pMove->next;
}
printf("\n");
}
8.主函数
代码如下(示例):
int main(void)
{
SingleList* a_list, * b_list, * a_myList, * b_myList, * c_list, * c_myList, * a_link_c, * b_link_c;
a_list = a_init_SingleList(); //初始化A链表
b_list = b_init_SingleList(); //初始化B链表
c_list = c_init_SingleList(); //初始化C链表
a_myList = create_SingleListByTail(a_list); //尾插法创建A链表
printf("链表A的内容为:\n");
printf_SingleList(a_myList);
b_myList = create_SingleListByTail(b_list); //尾插法创建B链表
printf("链表B的内容为:\n");
printf_SingleList(b_myList);
c_myList = create_SingleListByTail(c_list); //尾插法创建C链表
printf("链表C的内容为:\n");
printf_SingleList(c_myList);
a_link_c = link(a_myList, c_myList);
printf("链表A和链表C连接后为:\n");
printf_SingleList(a_link_c);
b_link_c = link(b_myList, c_myList);
printf("链表B和链表C连接后为:\n");
printf_SingleList(b_link_c);
searchCommonNodeFromSingleList(a_link_c, b_link_c); //寻找两个链表中的相同结点
system("pause");
return EXIT_SUCCESS;
}