1、如何判断一个链表是否有环?
2、如果有环的话,如何计算环的长度?
3、如何找到环的入口?
4、找到两条链表的第一个交点
1、现在先来解决第一个问题:
可以设置两个指针fast和slow都指向链表头,fast指针每次向前走两步,slow指针每次向前走一步。
如果fast指针遇到NULL了,说明该链表没有环。
如果链表有环,fasr指针每次都比slow指针走的快,最终两个指针会相遇。
bool IsCircle(ListNode *head)
{
//链表为空或链表只有结点就不用判断了
if(head==NULL || head->m_pNext==NULL)
return false;
ListNode *slow = head;
ListNode *fast = head;
while(fast!=NULL && fast->m_pNext!=NULL)
{
fast = fast->m_pNext->m_pNext;
slow = slow->m_pNext;
if(fast == slow)
{
break;
}
}
/*
跳出循环有两种情况:
1、要么是fast指针遇上NULL跳出循环,此时没环
2、有环,fast指针和slow指针相遇了
*/
if(fast==NULL || fast->m_pNext==NULL)
{
return false;//第一种情况
}
else
{
return true;//第二种情况
}
}
ListNode *Search(ListNode *head,int key)
{
for(ListNode *p=head;p!=NULL;p=p->m_pNext)
{
if(p->m_nKey == key)
return p;
}
return NULL;
}
int main()
{
//先测试没有环的链表
ListNode *head = NULL;
for(int i=0;i<10;i++)
{
InsertTail(&head,i);
}
Show(head);
bool flag = IsCircle(head);
if(!flag)
cout<<"没有环"<<endl;
else
cout<<"有环"<<endl;
//测试有环的链表
//构造环
ListNode *p = Search(head,2);
ListNode *q = Search(head,9);
q->m_pNext = p;
flag = IsCircle(head);
if(!flag)
cout<<"没有环";
else
cout<<"有环";
return 0;
}
2、关于环的长度
从第一次相遇时开始计数,到第二次相遇时结束计数
int GetCircleLength(ListNode *head)
{
ListNode *fast = head;
ListNode *slow = head;
int count = 0;//环的长度
int time = 0;//第几次相遇
while(1)
{
fast = fast->m_pNext->m_pNext;
slow = slow->m_pNext;
if(fast == slow)
{
time++;
}
if(time == 1)//开始计数
{
count++;
}
if(time == 2)//第二次相遇,结束计数
return count;
}
return -1;
}
int main()
{
ListNode *head = NULL;
for(int i=0;i<10;i++)
{
InsertTail(&head,i);
}
//构造环
ListNode *p = Search(head,2);
ListNode *q = Search(head,9);
q->m_pNext = p;
cout<<"环的长度:"<<GetCircleLength(head)<<endl;
return 0;
}
3、环的入口点
碰撞点p到连接点的距离=头指针到连接点的距离,因此,分别从碰撞点、头指针开始走,相遇的那个点就是连接点。
(1)当fast与slow相遇时,show肯定没有走完链表,而fast已经在还里走了n(n>= 1)圈。假设slow走了s步,那么fast走了2s步。fast的步数还等于s走的加上环里转的n圈,所以有:
2s = s + nr。因此,s = nr。
(2)设整个链表长为L,入口据相遇点X,起点到入口的距离为a。因为slow指针并没有走完一圈,所以:
a + x = s,带入第一步的结果,有:a + x = nr = (n-1)r + r = (n-1)r + L - a;即:
a = (n-1)r + L -a -x;
这说明:从头结点到入口的距离,等于转了(n-1)圈以后,相遇点到入口的距离。因此,我们可以在链表头、相遇点各设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。
ListNode * findCircleEntrance(ListNode *head)
{
ListNode *fast = head;
ListNode *slow = head;
while(fast!=NULL && fast->m_pNext!=NULL)
{
fast = fast->m_pNext->m_pNext;
slow = slow->m_pNext;
if(fast == slow)
{
break;
}
}
if(fast==NULL || fast->m_pNext==NULL)
return NULL;//说明没环
slow = head;
while(slow != fast)
{
slow = slow->m_pNext;
fast = fast->m_pNext;
}
return slow;
}
int main()
{
ListNode *head = NULL;
for(int i=0;i<10;i++)
{
InsertTail(&head,i);
}
//构造环
ListNode *p = Search(head,2);
ListNode *q = Search(head,9);
q->m_pNext = p;
ListNode *s = findCircleEntrance(head);
if(s != NULL)
cout<<"环的入口:"<<s->m_nKey<<endl;
return 0;
}
///
4、找到两条链表的第一个交点?????????????
(1)首先要判断两条链表是否有环,如果一条链表有环,一条链表没环,那肯定就没有交点了
(2)如果链表有环,断开环(断开环之前要保存下一个结点的值,因为之后还要还原)
(3)现在两条链表都没有环,求出两个链表的长度。
(4)将两条链表相差的长度先走完,两个链表再一起走,相遇的第一点即为第一个交点
(5)还原环,判断是否有交点
int GetLength(ListNode *head)//得到链表长度
{
int count = 0;
for(ListNode *p=head;p!=NULL;p=p->m_pNext)
count++;
return count;
}
ListNode *FirstIntersection(ListNode *head1,
ListNode *head2)
{
//判断head1和head2是否有环
ListNode *p = findCircleEntrance(head1);
ListNode *q = findCircleEntrance(head2);
//一个有环另一个没环则一定不相交
if( (p==NULL && q!=NULL) || (p!=NULL && q==NULL))
return NULL;
//用于保存断开环之前入口的下一个结点
ListNode *pnext = NULL;
ListNode *qnext = NULL;
if(p != NULL)//p有环则将环断开,之后需要还原
{
pnext = p->m_pNext;
p->m_pNext = NULL;
}
if(q != NULL)//q有环则将环断开,之后需要还原
{
qnext = q->m_pNext;
q->m_pNext = NULL;
}
//两个链表都没有环
int len1 = GetLength(head1);
int len2 = GetLength(head2);
ListNode *r = head1->m_pNext;
ListNode *s = head2->m_pNext;
/*将head1和head2的多余的长度先走完,两个链表再一起走,
相遇的第一点即为第一个交点*/
while(len1 > len2)
{
r = r->m_pNext;
len1--;
}
while(len2 > len1)
{
s = s->m_pNext;
len2--;
}
//多余的长度走完了,两个链表一起走,查找交点
while(r!=NULL && s!=NULL)
{
if(r == s)
{
break;
}
r = r->m_pNext;
s = s->m_pNext;
}
//将断开的环还原
if(pnext != NULL)
{
p->m_pNext = pnext;
}
if(qnext != NULL)
{
q->m_pNext = qnext;
}
//判断是否有交点
if(r==s && r!=NULL)
return r;
else
return NULL;
}
int main()
{
//1 先测试两条不相交的链表(没有环)
ListNode *head1 = NULL;
ListNode *head2 = NULL;
for(int i=0;i<10;i++)//构造两条不相交的链表
{
InsertTail(&head1,i);
InsertTail(&head2,i+20);
}
Show(head1);
Show(head2);
cout<<"两条无环不相交的链表:";
ListNode *p = FirstIntersection(head1,head2);
if(p == NULL)
cout<<"没有交点"<<endl;
else
cout<<"第一个交点为"<<p->m_nKey<<endl;
cout<<endl<<endl;
//2 构造环 测试一个有环的链表和一个没环的链表
ListNode *r = Search(head1,2);
ListNode *s = Search(head1,9);
s->m_pNext = r;
cout<<"一个有环的链表和一个没环的链表:";
p = FirstIntersection(head1,head2);
if(p == NULL)
cout<<"没有交点"<<endl;
else
cout<<"第一个交点为"<<p->m_nKey<<endl;
//看环是否还原
bool flag = IsCircle(head1);
if(!flag)
cout<<"没有环"<<endl;
else
cout<<"有环"<<endl;
cout<<endl<<endl;
//3 测试两条没有环,有交点的链表
ListNode *head3 = NULL;
ListNode *head4 = NULL;
for(int i=0;i<10;i++)//构造两条不相交的链表
{
InsertTail(&head3,i);
}
InsertTail(&head4,20);
InsertTail(&head4,21);
p = Search(head3,3);
ListNode * q = Search(head4,20);
assert(p!=NULL && q!=NULL && q->m_pNext!=NULL);
q->m_pNext = p;//3为两条链的第一个交点
cout<<"两条没有环,有交点的链表:";
p = FirstIntersection(head3,head4);
if(p == NULL)
cout<<"没有交点"<<endl;
else
cout<<"第一个交点为"<<p->m_nKey<<endl;
Show(head3);
Show(head4);
cout<<endl<<endl;
//构造两个个相交的且有环的链表
p = Search(head3,5);
q = Search(head3,9);
assert(p!=NULL && q!=NULL && q->m_pNext==NULL);
q->m_pNext = p; //构造环
cout<<"两条有环,有交点的链:";
p = FirstIntersection(head3,head4);
if(p == NULL)
cout<<"没有交点"<<endl;
else
cout<<"第一个交点为"<<p->m_nKey<<endl;
return 0;
}