给出俩个单向链表的头指针,比如h1,h2,判断这俩个链表是否相交。为了简化问题,我们假设俩个链表均不带环。
问题扩展:
如果链表可能有环列?
问题扩展:
如果链表可能有环列?
如果需要求出俩个链表相交的第一个节点列?
#include <stdio.h>
#include <stdlib.h>
typedef int Type; /* 字符型数据*/
typedef struct LNode {
Type data;
struct LNode *next;
} LNode;
LNode* CreateListNode(int value) {
LNode* pNode = (LNode *) malloc(sizeof(LNode));
pNode->data = value;
pNode->next = NULL;
return pNode;
}
void ConnectListNodes(LNode* pCurrent, LNode* pNext) {
if (!pCurrent) {
printf("Error to connect two nodes.\n");
exit(1);
}
pCurrent->next = pNext;
}
void DestroyNode(LNode* pNode) {
free(pNode);
}
unsigned int GetListLength(LNode* pHead) {
unsigned int nLength = 0;
LNode* pNode = pHead;
while (pNode != NULL) {
++nLength;
pNode = pNode->next;
}
return nLength;
}
//是否存在环,若存在返回入环点,否则返回空
LNode* isExistLoop(LNode *head, LNode **meetPoint) {
if (!head) {
return NULL;
}
LNode *inLoopPoint = NULL;
LNode *slow = head, *fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) {
break;
}
}
if (!fast || !(fast->next)) {
return NULL;
}
*meetPoint = fast;
slow = head;
while (slow != fast) {
slow = slow->next;
fast = fast->next;
}
inLoopPoint = fast;
return inLoopPoint;
}
LNode* getLastNode(LNode* pHead) {
LNode *p = pHead;
while (p && p->next) {
p = p->next;
}
return p;
}
/* 均无环,方式1:
* 长链表节点先出发前进(lengthMax-lengthMin)步,
* 之后两个链表同时前进,每次一步,相遇的第一点
* 即为两个链表相交的第一个点*/
LNode* FindFirstCommonNode1(LNode *pHead1, LNode *pHead2) {
// 得到两个链表的长度
unsigned int nLength1 = GetListLength(pHead1);
unsigned int nLength2 = GetListLength(pHead2);
int nLengthDif = nLength1 - nLength2;
LNode* pListHeadLong = pHead1;
LNode* pListHeadShort = pHead2;
if (nLength2 > nLength1) {
pListHeadLong = pHead2;
pListHeadShort = pHead1;
nLengthDif = nLength2 - nLength1;
}
// 先在长链表上走几步,再同时在两个链表上遍历
int i;
for (i = 0; i < nLengthDif; ++i)
pListHeadLong = pListHeadLong->next;
//第一个相等的结点
while ((pListHeadLong != NULL) && (pListHeadShort != NULL)
&& (pListHeadLong != pListHeadShort)) {
pListHeadLong = pListHeadLong->next;
pListHeadShort = pListHeadShort->next;
}
// 得到第一个公共结点
LNode* pFisrtCommonNode = pListHeadLong;
return pFisrtCommonNode;
}
/* 均无环,,方式2:
* 将其中一个链表首尾相连,
* 判断另一个链表是否存在环,
* 如果存在,则两个链表相交,
* 且找出来的环入口点即为相交的第一个点
*/
LNode* FindFirstCommonNode2(LNode* pHead1, LNode* pHead2) {
LNode *meetPoint = NULL, *inLoopPoint;
if (!isExistLoop(pHead1, &meetPoint) && !isExistLoop(pHead2, &meetPoint)) {
LNode *lastNode = getLastNode(pHead1);
//头尾相接制造环
lastNode->next = pHead1;
}
//若相交, pHead2定会出现环
return isExistLoop(pHead2, &meetPoint);
}
/*均有环
* 我们能够分别找出两个链表的相遇点pos1, pos2,
* 然后还是使用两个指针fast和slow,都初始化为pos1,
* 且fast每次前进2步,slow每次前进1步。
* 若fast指针在遇到slow前,出现fast等于pos2或fast->next等于pos2,
* 则说明两个链表相交,否则不相交。若两链表相交,
* 我们可知pos2肯定是两个链表的一个相交点,
* 将这个点看做两个链表的终止节点,使用(3)中的解法,
* 即可找到两个链表相交的第一个节点。
* */
LNode* FindFirstCommonNode3(LNode* pHead1, LNode* pHead2) {
LNode *meetPoint1 = NULL, *meetPoint2 = NULL;
LNode *fast = NULL, *slow = NULL;
if (isExistLoop(pHead1, &meetPoint1) && isExistLoop(pHead2, &meetPoint2)) {
fast = meetPoint1;
slow = meetPoint1;
do {
if (fast == meetPoint2 || fast->next == meetPoint2) {
break;
}
fast = fast->next->next;
slow = slow->next;
} while (fast != slow);
if (fast != meetPoint2 && fast->next != meetPoint2) {
// printf("Failed.\n");
return NULL;
} else {
int len1 = 0, len2 = 0;
LNode *longHead = pHead1, *shortHead = pHead2;
while (longHead->next != meetPoint2) {
len1++;
longHead = longHead->next;
}
while (shortHead->next != meetPoint2) {
len2++;
shortHead = shortHead->next;
}
int lenDif = len1 - len2;
//指针重置至起点
longHead = pHead1;
shortHead = pHead2;
if (len2 > len1) {
longHead = pHead2;
shortHead = pHead1;
lenDif = len2 - len1;
}
// 先在长链表上走几步,再同时在两个链表上遍历
int i;
for (i = 0; i < lenDif; ++i)
longHead = longHead->next;
//第一个相等的结点
while ((longHead != NULL) && (shortHead != NULL)
&& (longHead != shortHead)) {
longHead = longHead->next;
shortHead = shortHead->next;
}
// 得到第一个公共结点
LNode* pFisrtCommonNode = longHead;
// printf("first common node:%3d", *pFisrtCommonNode);
return pFisrtCommonNode;
}
}
}
LNode* FindFirstCommonNode(LNode* pHead1, LNode* pHead2) {
if (!pHead1 || !pHead2) {
return NULL;
}
LNode *meetPoint1 = NULL, *meetPoint2 = NULL;
//一个有环,另一个无环,必无公共点
if ((!isExistLoop(pHead1, &meetPoint1) && isExistLoop(pHead2, &meetPoint2))
|| (isExistLoop(pHead1, &meetPoint1)
&& !isExistLoop(pHead2, &meetPoint2))) {
return NULL;
}
//均无环
if (!isExistLoop(pHead1, &meetPoint1)
&& !isExistLoop(pHead2, &meetPoint2)) {
return FindFirstCommonNode1(pHead1, pHead2);
//return FindFirstCommonNode2(pHead1, pHead2);
}
//均有环
if (isExistLoop(pHead1, &meetPoint1) && isExistLoop(pHead2, &meetPoint2)) {
return FindFirstCommonNode3(pHead1, pHead2);
}
}
void Test(char* testName, LNode* pHead1, LNode* pHead2, LNode* pExpected) {
if (testName != NULL)
printf("%s begins: ", testName);
LNode* pResult = FindFirstCommonNode(pHead1, pHead2);
if (pResult == pExpected)
printf("first common node:%-3dPassed.\n", *pResult);
else
printf("Failed.\n");
}
//无环
void Test1() {
LNode* pNode1 = CreateListNode(1);
LNode* pNode2 = CreateListNode(2);
LNode* pNode3 = CreateListNode(3);
LNode* pNode4 = CreateListNode(4);
LNode* pNode5 = CreateListNode(5);
LNode* pNode6 = CreateListNode(6);
LNode* pNode7 = CreateListNode(7);
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode6);
ConnectListNodes(pNode4, pNode5);
ConnectListNodes(pNode5, pNode6);
ConnectListNodes(pNode6, pNode7);
Test("Test1", pNode1, pNode4, pNode6);
DestroyNode(pNode1);
DestroyNode(pNode2);
DestroyNode(pNode3);
DestroyNode(pNode4);
DestroyNode(pNode5);
DestroyNode(pNode6);
DestroyNode(pNode7);
}
//均有环
void Test2() {
LNode* pNode1 = CreateListNode(1);
LNode* pNode2 = CreateListNode(2);
LNode* pNode3 = CreateListNode(3);
LNode* pNode4 = CreateListNode(4);
LNode* pNode5 = CreateListNode(5);
LNode* pNode6 = CreateListNode(6);
LNode* pNode7 = CreateListNode(7);
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode6);
ConnectListNodes(pNode4, pNode5);
ConnectListNodes(pNode5, pNode6);
ConnectListNodes(pNode6, pNode7);
//制造环
ConnectListNodes(pNode7, pNode4);
Test("Test2", pNode1, pNode4, pNode6);
DestroyNode(pNode1);
DestroyNode(pNode2);
DestroyNode(pNode3);
DestroyNode(pNode4);
DestroyNode(pNode5);
DestroyNode(pNode6);
DestroyNode(pNode7);
}
int main(void) {
setvbuf(stdout, NULL, _IONBF, 0);
Test1();
return EXIT_SUCCESS;
}