注:本篇博客中所使用单链表基本操作函数均可在《线性数据结构——链表》中找到,链接https://blog.csdn.net/lw13572259173/article/details/81281601
单链表实现约瑟夫环
约瑟夫环:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
pNode LinkListToJoseph(pList * pplist)//单链表实现约瑟夫环
{
assert(pplist);
int N = 2; //报数(m)
//1.先将链表变成环
LinkListFormRing(pplist);
//2.编号为1的人开始报数,删除数到N的结点
pList cur = *pplist;
while (cur != cur->next)
{
//报数
for (int i = 1; i < N; i++)
{
cur = cur->next;
}
//删除
cur->data = cur->next->data;//把下一个的值赋给这一个
pList del = cur->next;//保存下一个
cur->next = cur->next->next;//指向下一个的下一个
free(del);
del = NULL;
}
//4.直到只剩下一个结点(结点自己指向自己),返回
return cur;
}
void LinkListFormRing(pList * pplist)//单链表成环
{
//让尾节点指向头结点
assert(pplist);
pList cur = *pplist;
while (cur->next != NULL)
{
cur = cur->next;
}
cur->next = *pplist;
}
判断链表是否带环,若带环,求环长度,求环入口点
带环:双指针,一个指针一次走一步,一个指针一次走两步,若两个指针相遇,则返回相遇点,若快指针=NULL,则不带环
pList LinkListRing(pList plist) //判断链表是否带环
{
assert(plist);
pList str1 = plist;//快指针
pList str2 = plist;//慢指针
while (NULL != str1)
{
str1 = str1->next->next;
str2 = str2->next;
if (str1 == str2) //两个指针相遇
{
return str1;
}
}
return NULL;
}
返回结点个数:相遇点,慢指针再相遇点不动,快指针走并计数,直到再次相遇时返回
int LinkListRingLength(pList plist, pList cur)//求链表环长度
{
assert(plist && cur);
pList str1 = cur->next;//cur 相遇点
int count = 0;
while (str1 != cur)
{
str1 = str1->next;
count++;
}
return count + 1; //加上相遇点
}
入口点:一个指针在起始位置,一个指针在相遇点,两指针同时走,相遇时,即为环起始点
pList LinkListRingEntrance(pList plist, pList cur)//求链表环入口点
{
pList str1 = plist;
while (str1 != cur)//两指针没相遇
{
str1 = str1->next;
cur = cur->next;
}
return str1;//两指针相遇返回
}
判断两个链表是否相交,若相交,求交点
这类问题,主要分为两种情况
1.链表不可能带环
链表不可能带环,有三种可能性
相交:两个链表均走到最后一个结点,看是否相等
int LinkListIntersect(pList plist1, pList plist2)//判断两个链表是否相交
{
assert(plist1 && plist2);
while (NULL != plist1->next) //找到最后一个结点
{
plist1 = plist1->next;
}
while (NULL != plist2->next) //找到最后一个结点
{
plist2 = plist2->next;
}
if (plist1 == plist2)
{
return 1;
}
return 0;
}
交点:求两个链表长度,长的走两个链表之差,再两个同时走
pList LinkListIntersection(pList plist1, pList plist2)//求两个链表交点
{
assert(plist1 && plist2);
int count1 = 0;
int count2 = 0;
pList cur1 = plist1;
pList cur2 = plist2;
while (NULL != cur1) //求链表1的长度
{
cur1 = cur1->next;
count1++;
}
while (NULL != cur2) //求链表2的长度
{
cur2 = cur2->next;
count2++;
}
cur1 = plist1;
cur2 = plist2;
if (count1 > count2)//长的走两链表个数之差
{
for (int i = 0; i < count1 - count2; i++)
{
cur1 = cur1->next;
}
}
else
{
for (int i = 0; i < count2 - count1; i++)
{
cur2 = cur2->next;
}
}
while (cur1 != cur2)//两个同时走
{
cur1 = cur1->next;
cur2 = cur2->next;
}
return cur1;
}
2.链表可能带环
当链表带环时,有两种情况
相交、交点(两个链表可能带环)
先判断两个链表是否带环(一个带环一个不带环的情况不存在),带环,分别求两个带环链表的入口点,判断两个链表的入口点是否相同,若相同,则交点在环外,将两个链表改造为Y型求交点;若不相同,则一个指针在链表1的入口点不动,另外一个指针在链表2的入口点开始遍历链表,看是否能够遇到链表1的入口点
pList LinkListRingIntersect(pList plist1, pList plist2)
{
//判断链表1,2是否带环
pList cur1 = LinkListRing(plist1);//返回交点
pList cur2 = LinkListRing(plist2);
if (cur1 && cur2)//都带环
{
//求环入口点
pList str1 = LinkListRingEntrance(plist1, cur1);//返回入口点
pList str2 = LinkListRingEntrance(plist2, cur2);
//判断入口点是否相等
if (str1 == str2) //相等,相交,交点在环外,开头到入口点改造成Y形
{
str1->next = NULL;
str2->next = NULL;
//求两个链表交点
return LinkListIntersection(plist1, plist2);
}
else //不相等,判断是否相交
{
//从入口点开始遍历链表1,看会不会遇到链表2的入口点
pList str11 = str1->next;
while (str11 != str1)
{
if (str11 == str2)
{
return str2;//交点在环上
}
str11 = str11->next;
}
return NULL;
}
}
else if (cur1 == NULL && cur1 == NULL)//不带环
{
if (LinkListIntersect(plist1, plist2))//判断两个链表是否相交
{
return LinkListIntersection(plist1, plist2);//交点
}
return NULL;
}
else
{
return NULL;
}
}