【正确答案】
【答案解析】单链表有环是指单链表中某个结点的next指针域指向的是链表中在它之前的某一个结点,这样在链表的尾部形成一个环形结构。检测单链表是否有环,一般有以下几种方法。
方法一:定义一个指针数组,初始化为空指针,从链表的头指针开始往后遍历,每次遇到一个指针就跟指针数组中的指针相比较,若没有找到相同的指针,说明这个结点是第一次访问,还没有形成环,将这个指针添加到指针数组中去。若在指针数组中找到了同样的指针,说明这个结点已经被访问过了,于是就形成了环。
方法二:定义两个指针fast与slow,slow的初始值指向头结点,fast的初始值指向头结点的下一点,slow每次前进一步,fast每次前进两步,两个指针同时向前移动,快指针每移动一次都要跟慢指针比较,直到当快指针等于慢指针为止,就证明这个链表是带环的单向链表,否则证明这个链表是不带环的循环链表(fast先行到达尾部为NULL,则为无环链表)。
struct listtype
{
int data;
struct listtype *next;
};
typedef struct listtype *list;
int IsLoop(list s11)
{
list fast=s11->next;∥较slow快一步,步长为1
list slow=s11;
if(fast==NULL‖fast==slow)∥判断链表长为2时是否相交
return -1;
while(fast!=NULL)
{
fast=fast->next;
slow=slow->next;
if(fast==slow)
return 1;
}
}
方法三:通过使用STL库中的map表进行映射。首先定义map<node*,int>m;将一个node*指针映射成数组的下标,并赋值为一个int类型的数值。然后从链表的头指针开始往后遍历,每次遇到一个指针P,就判断m[p]是否为0。如果为0,则将m[p]赋值为1,表示该结点是第一次访问;而如果m[p]的值为1,则说明这个结点已经被访问过一次了,于是就形成了环。
程序代码示例如下:
map<node*,int>m;
bool IsLoop(node *head)
{
if(!head)
return false;
node *p=head;
while(p)
{
if(m[p]==0)∥默认值都是0
m[p]=1;
else if(m[p]==1)
return true;
p=p->next;
}
}
如果单链表有环,按照方法二的思路,走得快的指针fast若与走得慢的指针slow相遇时,slow指针肯定没有遍历完链表,而fast指针已经在环内循环了n圈(1≤n)。假设slow指针走了s步,则fast指针走了2s步(fast步数还等于S加上在环上多转的n圈),设环长为r,则满足如下关系表达式:
2s=s±nr
s=nr
设整个链表长为L,入口环与相遇点距离为x,起点到环入口点的距离为a。则满足如下关系表达式:
a+x=nr
a+x=(n-1)r+r=(n-1)r+L-a
a=(n-1)r+(L-a-x)
(L-a-x)为相遇点到环入口点的距离,从链表头到环入口点的距离=(n-1)*循环内环+相遇点到环入口点,于是从链表头与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。
程序代码如下:
list* FindLoopPort(list *head)
{
list *slow=head,*fast=head;
while(fast && fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)break;
}
if(fast==NULL‖fast->next==NULL)
return NULL;
slow=head;
while(slow!=fast)
{
slow=slow->next;
fast=fast->next;
}
return slow;
}