求两个单链表相交的交点— 链表不带环
不带环的两个链表,只有两种情况。一种是平行也就是不相交,要不然就会相交形成Y型或者头尾相连的一字型。而只要相交,两个链表的尾结点一定是相同的,所以思路很清晰,判断尾结点是否相等就知道是否相交。
求交点:用两个指针,让指向较长的链表的指针先走长与短链表的长度差步,然后两个指针同时走,等到两个指针指向了同一个结点,那就是交点。
//求两个单链表相交的交点--- 链表不带环
pNode GetCorssNode(pNode pHead1, pNode pHead2)
{
if (NULL == pHead1 || NULL == pHead2) {
return NULL;
}
int len1 = 0;
int len2 = 0;
pNode pCur1 = pHead1;
pNode pCur2 = pHead2;
while (pCur1) {
len1++;
pCur1 = pCur1->pNext;
}
while (pCur2) {
len2++;
pCur2 = pCur2->pNext;
}
int diff = len1 - len2;
while (0 != diff) {
if (diff > 0) {
pHead1 = pHead1->pNext;
diff--;
}
else {
pHead2 = pHead2->pNext;
diff++;
}
}
//走到这里两个指针距相交点有相同距离
while (pHead1 != pHead2) {
pHead1 = pHead1->pNext;
pHead2 = pHead2->pNext;
}
return pHead1;
}
判断链表是否带环,返回相遇点
如果一个指针每次走一步,一个指针每次走两步,那么快的指针一定会追上慢的指针。如果快指针走到NULL了,那么肯定不带环。
所以按这个思路,返回快指针追上满指针的结点就可以了,这个相遇点一定在环里。
// 判断链表是否带环,返回相遇点---注意推导公式
pNode IsListWithCircle(pNode pHead1)
{
if (NULL == pHead1) {
return NULL;
}
//一快一慢,若相遇则有环,若快的走到空就不带环
pNode pFst = pHead1;
pNode pLow = pHead1;
while (pFst && pFst->pNext) {
pFst = pFst->pNext->pNext;
pLow = pLow->pNext;
if (pFst == pLow) {
return pFst;
}
}
return NULL;
}
获取环的入口点
方法:一个指针指向相遇点,另一个指针指向链表头结点,两个指针同时走,当两个指针指向相同,则为入口点。
证明:
对遇点列方程,慢指针的走到遇点的路程 * 2 = 快指针走到遇点经过的路程
2 *( L + X) = L + X + n*R
扫描二维码关注公众号,回复: 180918 查看本文章其中n是快指针可能已经绕的圈数,因为如果L特别长,快指针就已经饶了n圈. 1 <= n
解得: L = n*R -X = (n -1) *R + R- X
上式如果模R,余 R - X,
也就是说如果一个指针从头走,一个指针从遇点走,不论指向遇点的指针走多少圈,最后一定会在入口点相遇。
证毕!
// 获取环的入口点
pNode GetCircleEnter(pNode pHead1, pNode pMeetNode)
{
if (NULL == pHead1 || NULL == pMeetNode) {
return NULL;
}
//两个同速指针,一个从起点一个从相遇点走
pNode pStr = pHead1;
while (pStr != pMeetNode) {
pStr = pStr->pNext;
pMeetNode = pMeetNode->pNext;
}
return pStr;
}
// 获取环的长度
int GetCircleLen(pNode pMeetNode)
{
if (NULL == pMeetNode) {
return -1;
}
pNode pCur = pMeetNode;
int count = 1;
pCur = pCur->pNext;
while (pCur != pMeetNode) {
count++;
pCur = pCur->pNext;
}
return count;
}
判断两个链表是否相交,链表可能带环
第一种情况,两个都不带环。用上述的方法判断就好了。
第二种情况,有一个带环。那一定不相交,因为只要两个相交了,那么环肯定是公共部分。
第三种情况,两个都带环。有两种相交的情况,相交点在环内和环外。
但是处理方法是一样的,分别找到两个链表在环里的相遇点,让一个点不动,遍历一遍所在的环,如果找到另一个相遇点,那么两个遇点就是在一个环里————即两链表相交。
// 判断两个链表是否相交,链表可能带环
int IsSListCrossWithCircle(pNode pHead1, pNode pHead2)
{
if (NULL == pHead1 || NULL == pHead2) {
return -1;
}
pNode withCir1 = IsListWithCircle(pHead1);
pNode withCir2 = IsListWithCircle(pHead2);
//两个都没环
if (!withCir1 && !withCir2) {
return IsSListCross(pHead1, pHead2);
}
//两个都有环才有可能是相交的
if (withCir1 && withCir2) {
if (withCir1 == withCir2) {
return 2;
}
pNode pCur = withCir1;
pCur = pCur->pNext;
while (pCur != withCir1) {
if (pCur == withCir2) {
return 2;
}
pCur = pCur->pNext;
}
return -1;
}
//仅有一个有环,肯定不相交
return -1;
}