题目来自编程之美
题目:如何知道两个单链表(可能有环)是否相交
分析:根据两个链表是否有环来分别处理,具体参考单链表面试题
1、如果两个链表都没有环。可直接根据两个链表最后一个元素是否相等确定是否有环。
2、一个有环,一个没环。肯定不相交
3、两个都有环。(注,两个有环链表相交是指这个环属于两个链表共有)
这里重点求第三种情况:即判定两个都有环的链表是否相交。
思路:在链表A中找到环中一个结点,之后判断该结点是否在B中。此时该结点可以使用环中任意结点,为了方便,这里使用两个指针的碰撞点。
具体思路:在A链表上, 使用指针追赶的方法,找到两个指针碰撞点,之后判断碰撞点是否在B链表上。如果在,则相交 。
代码
struct ListNode
{
int m_Data;
ListNode* m_pNext;
};
/*思路:在A链表上,使用指针追赶的方法,找到两个指针碰撞点,之后判断碰撞点是否在B链表上。如果在,则相交 。*/
bool IsCrossing(ListNode* pFirstHead,ListNode* pSecHead)
{
assert(pFirstHead != NULL && pSecHead);
ListNode* pCurOfFirst = pFirstHead;
ListNode* pCurOfSec = pSecHead;
ListNode* pMeetNode = FindMeetNode(pCurOfFirst);//获得第一个链表的入口结点
int nCountOfSec = GetNodeCountOfList(pSecHead);//获得第二个带环链表中元素个数
//把第一个链表的入口点与第二个链表的每一个结点比较
for (int i = 1;i <= nCountOfSec;i++)
{
if (pMeetNode == pCurOfSec)
{
return true;
}
pCurOfSec = pCurOfSec->m_pNext;
}
return false;
}
/*
思路:统计第一个结点到碰撞点之间结点个数 + 统计环中结点个数
1、先找到碰撞点
2、利用碰撞点,找到环入口点,同时统计第一个结点到碰撞点之间结点个数
3、利用碰撞点或者环入口点,在环中转圈,统计环中结点个数
*/
int GetNodeCountOfList(ListNode* pHead)
{
assert(pHead != NULL);
ListNode* pFast = pHead;
ListNode* pSlow = pHead;
ListNode* pEntry = pHead;
int nCountOfBeforeEntry = 0; //统计第一个结点到碰撞点之间结点个数
int nCountOfCycle = 1; //统计环中结点个数
while (pFast->m_pNext && pFast->m_pNext->m_pNext)
{
pFast = pFast->m_pNext->m_pNext;
pSlow = pSlow->m_pNext;
if (pFast == pSlow)//找到碰撞点
{
//查找环的入口结点,并统计第一个结点到碰撞点之间结点个数
while (pEntry != pSlow)
{
pEntry = pEntry->m_pNext;
pSlow = pSlow->m_pNext;
nCountOfBeforeEntry++;
}
//统计环中结点个数
pSlow = pFast;
pFast = pFast->m_pNext;
while (pFast != pSlow)
{
pFast = pFast->m_pNext;
nCountOfCycle++;
}
return nCountOfBeforeEntry + nCountOfCycle;
}
}
return 0;
}
/*在快慢指针相遇处,使用两个指针分别从头结点和碰撞处开始走,相遇点就是环入口点*/
ListNode* FindMeetNode(ListNode* pHead)
{
assert(pHead != NULL);
ListNode* pFast = pHead;
ListNode* pSlow = pHead;
ListNode* pMeetNode = pHead;
while (pFast->m_pNext && pFast->m_pNext->m_pNext)
{
pFast = pFast->m_pNext->m_pNext;
pSlow = pSlow->m_pNext;
if (pFast == pSlow)//找到碰撞点
{
return pFast;
}
}
return NULL;
}
测试代码
#include <iostream>
#include <assert.h>
using namespace std;
struct ListNode
{
int m_Data;
ListNode* m_pNext;
};
/*
思路:统计第一个结点到碰撞点之间结点个数 + 统计环中结点个数
1、先找到碰撞点
2、利用碰撞点,找到环入口点,同时统计第一个结点到碰撞点之间结点个数
3、利用碰撞点或者环入口点,在环中转圈,统计环中结点个数
*/
int GetNodeCountOfList(ListNode* pHead)
{
assert(pHead != NULL);
ListNode* pFast = pHead;
ListNode* pSlow = pHead;
ListNode* pEntry = pHead;
int nCountOfBeforeEntry = 0; //统计第一个结点到碰撞点之间结点个数
int nCountOfCycle = 1; //统计环中结点个数
while (pFast->m_pNext && pFast->m_pNext->m_pNext)
{
pFast = pFast->m_pNext->m_pNext;
pSlow = pSlow->m_pNext;
if (pFast == pSlow)//找到碰撞点
{
//查找环的入口结点,并统计第一个结点到碰撞点之间结点个数
while (pEntry != pSlow)
{
pEntry = pEntry->m_pNext;
pSlow = pSlow->m_pNext;
nCountOfBeforeEntry++;
}
//统计环中结点个数
pSlow = pFast;
pFast = pFast->m_pNext;
while (pFast != pSlow)
{
pFast = pFast->m_pNext;
nCountOfCycle++;
}
return nCountOfBeforeEntry + nCountOfCycle;
}
}
return 0;
}
/*在快慢指针相遇处,使用两个指针分别从头结点和碰撞处开始走,相遇点就是环入口点*/
ListNode* FindMeetNode(ListNode* pHead)
{
assert(pHead != NULL);
ListNode* pFast = pHead;
ListNode* pSlow = pHead;
ListNode* pMeetNode = pHead;
while (pFast->m_pNext && pFast->m_pNext->m_pNext)
{
pFast = pFast->m_pNext->m_pNext;
pSlow = pSlow->m_pNext;
if (pFast == pSlow)//找到碰撞点
{
return pFast;
}
}
return NULL;
}
/*思路:在A链表上,使用指针追赶的方法,找到两个指针碰撞点,之后判断碰撞点是否在B链表上。如果在,则相交 。*/
bool IsCrossing(ListNode* pFirstHead,ListNode* pSecHead)
{
assert(pFirstHead != NULL && pSecHead);
ListNode* pCurOfFirst = pFirstHead;
ListNode* pCurOfSec = pSecHead;
ListNode* pMeetNode = FindMeetNode(pCurOfFirst);//获得第一个链表的入口结点
int nCountOfSec = GetNodeCountOfList(pSecHead);//获得第二个带环链表中元素个数
//把第一个链表的入口点与第二个链表的每一个结点比较
for (int i = 1;i <= nCountOfSec;i++)
{
if (pMeetNode == pCurOfSec)
{
return true;
}
pCurOfSec = pCurOfSec->m_pNext;
}
return false;
}
void CreateList(ListNode** pHead,int nLen)//头指针使用指针的指针
{
assert(*pHead == NULL && nLen > 0);
ListNode* pCur = NULL;
ListNode* pNewNode = NULL;
for (int i = 0;i < nLen;i++)
{
pNewNode = new ListNode;
cin>>pNewNode->m_Data;
pNewNode->m_pNext = NULL;
if (*pHead == NULL)
{
*pHead = pNewNode;
pCur = *pHead;
}
else
{
pCur->m_pNext = pNewNode;
pCur = pNewNode;
}
}
}
/*让第一个链表的尾指针指向第二个链表的第三个元素
如果第二个链表没有第三个元素,则两个链表不相交,
如果第二个链表有第三个元素,则两个链表交点在环外*/
void CreateCrossingList1(ListNode* pFirstHead,ListNode* pSecHead)
{
assert(pFirstHead != NULL && pSecHead);
ListNode* pCurOfFirst = pFirstHead;
ListNode* pCurOfSec = pSecHead;
//寻找第一个链表最后一个结点
while (pCurOfFirst->m_pNext)
{
pCurOfFirst = pCurOfFirst->m_pNext;
}
//第一个链表最后一个结点指向第二链表的第三个元素
if (pCurOfSec->m_pNext && pCurOfSec->m_pNext->m_pNext)
{
pCurOfFirst->m_pNext = pCurOfSec->m_pNext->m_pNext;
}
}
/*让第一个链表pFirstHead的尾指针指向第二个链表pSecHead的第四个元素
如果第二个链表没有第四个元素,则两个链表不相交,
如果第二个链表有第四个元素,则两个链表交点在环内*/
void CreateCrossingList2(ListNode* pFirstHead,ListNode* pSecHead)
{
assert(pFirstHead != NULL && pSecHead);
ListNode* pCurOfFirst = pFirstHead;
ListNode* pCurOfSec = pSecHead;
//寻找第一个链表最后一个结点
while (pCurOfFirst->m_pNext)
{
pCurOfFirst = pCurOfFirst->m_pNext;
}
//第一个链表最后一个结点指向第二链表的第三个元素
if (pCurOfSec->m_pNext && pCurOfSec->m_pNext->m_pNext && pCurOfSec->m_pNext->m_pNext->m_pNext)
{
pCurOfFirst->m_pNext = pCurOfSec->m_pNext->m_pNext->m_pNext;
}
}
/*判断是不是环*/
bool IsCycle(ListNode* pHead)
{
assert(pHead != NULL);
ListNode* pFast = pHead;
ListNode* pSlow = pHead;
while (pFast->m_pNext && pFast->m_pNext->m_pNext)
{
pFast = pFast->m_pNext->m_pNext;
pSlow = pSlow->m_pNext;
if (pFast == pSlow)
{
return true;
}
}
return false;
}
/*把链表最后一个结点指向第四个结点*/
void CreateCycle(ListNode* pHead)
{
assert(pHead);
ListNode* pLastNode = pHead;
while (pLastNode->m_pNext)
{
pLastNode = pLastNode->m_pNext;
}
if (pHead->m_pNext && pHead->m_pNext->m_pNext && pHead->m_pNext->m_pNext->m_pNext)
{
pLastNode->m_pNext = pHead->m_pNext->m_pNext->m_pNext;
}
}
int main()
{
int nLen = 0;
bool bIsCrossing = false;
//测试一,有交点且交点在环外
ListNode* pHeadFirst = NULL;
ListNode* pHeadSec = NULL;
//创建有环链表
cout<<"please input node num: ";
cin >> nLen;
CreateList(&pHeadFirst,nLen);
CreateCycle(pHeadFirst);
//创建有环链表
cout<<"please input node num: ";
cin >> nLen;
CreateList(&pHeadSec,nLen);
CreateCrossingList1(pHeadSec,pHeadFirst);//交点在环外
if (IsCycle(pHeadFirst) && IsCycle(pHeadSec))
{
cout<<"两个链表均有环!"<<endl;
bIsCrossing = IsCrossing(pHeadFirst,pHeadSec);
if (bIsCrossing)
{
cout<<"相交!"<<endl;
}
else
{
cout<<"不相交!"<<endl;
}
}
else
{
cout<<"两个链表不均有环!"<<endl;
}
//测试二,有交点且交点在环内
ListNode* pHeadFirst1 = NULL;
ListNode* pHeadSec1 = NULL;
//创建有环链表
cout<<"please input node num: ";
cin >> nLen;
CreateList(&pHeadFirst1,nLen);
CreateCycle(pHeadFirst1);
//创建有环链表
cout<<"please input node num: ";
cin >> nLen;
CreateList(&pHeadSec1,nLen);
CreateCrossingList2(pHeadSec1,pHeadFirst1); //交点在环内
if (IsCycle(pHeadFirst1) && IsCycle(pHeadSec1))
{
cout<<"两个链表均有环!"<<endl;
bIsCrossing = IsCrossing(pHeadFirst1,pHeadSec1);
if (bIsCrossing)
{
cout<<"相交!"<<endl;
}
else
{
cout<<"不相交!"<<endl;
}
}
else
{
cout<<"两个链表不均有环!"<<endl;
}
//测试三,均有环,但是无交点
ListNode* pHeadFirst2 = NULL;
ListNode* pHeadSec2 = NULL;
//创建有环链表
cout<<"please input node num: ";
cin >> nLen;
CreateList(&pHeadFirst2,nLen);
CreateCycle(pHeadFirst2);
//创建有环链表
cout<<"please input node num: ";
cin >> nLen;
CreateList(&pHeadSec2,nLen);
CreateCycle(pHeadSec2);
if (IsCycle(pHeadFirst2) && IsCycle(pHeadSec2))
{
cout<<"两个链表均有环!"<<endl;
bIsCrossing = IsCrossing(pHeadFirst2,pHeadSec2);
if (bIsCrossing)
{
cout<<"相交!"<<endl;
}
else
{
cout<<"不相交!"<<endl;
}
}
else
{
cout<<"两个链表不均有环!"<<endl;
}
system("pause");
return 1;
}