1.判断单链表是否带环
这个题一开始你可能会想,这不是很简单吗,直接用一个指针遍历一编,如果返回值为空就没有带环,如果死循环就带环。EMmmmmm,这样做没毛病,但是我们可以不这么暴力也可以判断链表是否带环。使用追赶的方法,设定两个指针slow、fast,从头指针开始,每次分别前进1步、2步。如存在环,则两者相遇;如不存在环,fast遇到NULL退出。
SListNode *SListIsCycle(SListNode *list)
{
SListNode *slow = list, *fast = list;
assert(list);
while ((fast != NULL) &&( fast->_next != NULL)) //如果无环,则fast先走到终点为空,当链表长度为奇数时,fast->Next为空 ,当链表长度为偶数时,fast为空
{
fast = fast->_next->_next;
slow = slow->_next;
if (fast == slow)
{
break;
}
}
if (fast == NULL || fast->_next == NULL)
{
return NULL;
}
else
{
return fast;
}
}
2. 假设单链表带环,求环长度
这个问题在上一道问题的基础上,上一道题找到并返回了fast指针和slow指针相遇的链表指针,而相遇点一定在环内,所以我们可以继续让fast和slow指针在相遇点同时走,但是每次slow指针走一步,fast指针走两步,当他们再次相遇时,slow指针刚好绕环走了一圈。
int SListCycleLen(SListNode* meetNode)
{
int len = 0;
SListNode* meet = SListIsCycle(meetNode),*fast=meet,*slow=meet;//调用上一个函数找到fast和slow指针的相遇点
assert(meet&&meetNode);
while (1)
{
slow = slow->_next;
fast = fast->_next->_next;
len++;
if (slow == fast)//当fast、slow相遇,返回len
{
return len;
}
}
}
3. 如果单链表带环,求环的入口
emmm,这是一道数学题,我们假设链表长度L,环长度R,入口点到链表头结点的距离a,入口点到快慢指针相遇点的距离x,现在假设慢指针走了S步与快指针相遇,
s = a + x;
那么快指针每次走了2S步,
2s = a + nr + x; //nr是有可能a很长在slow指针还没有入环时fast已经在环里走了很多圈
就可以得到:a + x = nr;
a = nr - x;
可以看出来,头节点到入口点的距离=相遇点到入口点的距离,那我们让两个指针,一个从相遇点走,一个从头节点走,最后一定在入口点相遇。
int SListEntryNode(SListNode* list, SListNode* meetNode)//传参传的是头指针和相遇点的指针
{
assert(list&&meetNode);
SListNode* p = list, *q = meetNode;
while (1)
{
p = p->_next;
q = q->_next;
if (p == q)
{
return q->_data ;
}
}
}
完整的代码:
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int DataType;
typedef struct SListNode
{
struct SListNode *_next;
DataType _data;
}SListNode;
//创建结点
SListNode *BuySListNode(DataType x)
{
SListNode *Lhead = (SListNode *)malloc(sizeof(SListNode));
Lhead->_data = x;
Lhead->_next = NULL;
return Lhead;
}
//销毁链表
void SListDestory(SListNode* *PHead)
{
assert(*PHead);
SListNode *pre = *PHead, *p = (*PHead)->_next;
while (p != NULL)
{
free(pre);
pre = p;
p = pre->_next;
}
free(pre);
}
//尾插数据
void SListPushBack(SListNode* *ppHead, DataType x)
{
SListNode* s = NULL;
if (!(*ppHead))
{
(*ppHead) = BuySListNode(x);
}
else
{
s = *ppHead;
while (s->_next)
{
s = s->_next;
}
s->_next = BuySListNode(x);
}
}
//单链表变循环单链表
SListNode *DanHuan(SListNode * *PHead)
{
SListNode *p = *PHead;
assert(*PHead);
while (p->_next)
{
p = p->_next;
}
p->_next = (*PHead)->_next->_next;
return PHead;
}
//判断单链表是否带环
SListNode *SListIsCycle(SListNode *list)
{
SListNode *slow = list, *fast = list;
assert(list);
while ((fast != NULL) && (fast->_next != NULL))
{
fast = fast->_next->_next;
slow = slow->_next;
if (fast == slow)
{
break;
}
}
if (fast == NULL || fast->_next == NULL)
{
return NULL;
}
else
{
return fast;
}
}
//若单链表带环,求环长度
int SListCycleLen(SListNode* meetNode)
{
int k = 0;
SListNode* meet = SListIsCycle(meetNode), *fast = meet, *slow = meet;
assert(meet&&meetNode);
while (1)
{
slow = slow->_next;
fast = fast->_next->_next;
k++;
if (slow == fast)
{
return k;
}
}
}
//求环的入口
int SListEntryNode(SListNode* list, SListNode* meetNode)
{
assert(list&&meetNode);
SListNode* p = list, *q = meetNode;
while (1)
{
p = p->_next;
q = q->_next;
if (p == q)
{
return q->_data;
}
}
}
int main()
{
SListNode *PHead = NULL;
SListNode *PHead1 = NULL;
SListPushBack(&PHead, 1);
SListPushBack(&PHead, 3);
SListPushBack(&PHead, 5);
SListPushBack(&PHead, 6);
SListPushBack(&PHead, 7);
SListPushBack(&PHead, 8);//用尾插法形成一个单链表
printf("*********************\n");
//if (SListIsCycle(DanHuan(&PHead)) == 0)//判断单链表是否有环
//{
//printf("NO\n");
//}
//else
//{
//printf("Yes\n");
//}//在调试下面两个函数时,判断单链表是否有环应当屏蔽掉
printf("*********************\n");
printf("%d\n", SListCycleLen(DanHuan(&PHead)));//如果有环,求环长
printf("*********************\n");
printf("%d\n", SListEntryNode(PHead, SListIsCycle(PHead)));//求环的入口点
system("pause");
return 0;
}