***单链表面试题系列之约瑟夫环***
约瑟夫环
关于约瑟夫环的故事由来,大家可以百度了解一下,很有意思的数学问题!
我在本篇博客中是从1开始编号的;所以 假设有n个人,每次报数为 m 的人删除;注意:报数为 m 和 编号 n 没有关系,不要搞混了!
示意图: 其中time1~7 表示该节点在第几次数到 3 时被删除!
上图是一个综合分析的图,比较杂糅,下面我们列出分步图,假设圆桌共有 n = 8 个人,每次删除报数为 m = 3的人,对八个人分别进行编号为1~8,假设1是第一个结点开始报数,步骤如下:
以前做过删除链表的非尾结点的例题的话,解这道题就很容易了!
删除单链表的非尾结点的链接:http://blog.csdn.net/bitboss/article/details/51607731
看到这道约瑟夫环的问题时,很自然地就可以联想到用链表来求解吧! 那么,从第一个结点开始,假设是第一个人,报数从1开始,每次报到 m 的人就被杀死,对应到链表里就是每走 m-1 步后指向的结点被删除,
(为什么是m-1, 用m = 1来类比就可以得到,第一个人就报的是1,那么此时指针指向的就是1,没有动,以此类推,而删除的其实也不是该结点,而是类似删除一个非尾结点的方法直接交换下一个结点的data,然后删除下一个结点,因为是环,所以可以对应到非尾结点,效率比较高)
然后又从此时指向的结点从1开始数, 数到 m 又删除, 以此类推,最后剩余的结点就是获胜者!
代码实现:(代码中将完整的代码列出,请仔细阅读注释,了解每个函数的功能)
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int DataType;
typedef struct LinkNode
{
DataType data;
struct LinkNode* next;
}LinkNode,*pLinkNode;//结点结构体
typedef struct LinkList
{
LinkNode* pHead;//头结点指针
LinkNode* pback;
}LinkList ,*pLinkList;//链表
void PushBack(pLinkList pList,DataType x)
{
pLinkNode cur = NULL;
pLinkNode pvr = NULL;
pLinkNode newNode = (pLinkNode)malloc(sizeof(LinkNode ));
if(newNode == NULL)
{
printf("out of memory\n");
exit(0);
}
assert(pList);
cur = pList ->pback ;
newNode ->data = x;
newNode ->next = NULL;
if(cur == NULL)
{
pList->pHead = newNode;
pList->pback = newNode ;
}
else
{
cur->next = newNode ;
pList->pback = newNode ;
}
}//尾插
void InitLinkList(pLinkList pList)
{
assert(pList);
pList->pHead = NULL;
pList->pback = pList->pHead ;
}//初始化列表
//构造一个环链表;
void MakeRing(pLinkList plist1)
{
pLinkNode cur = NULL;
pLinkNode pvr = NULL;
assert(plist1);
cur = plist1 ->pHead ;
while(cur)
{
pvr = cur;
cur = cur->next ;
}
if(pvr != NULL)
pvr->next = plist1 ->pHead ;//找到尾结点,让它可以指向前面第一个结点!
}
pLinkNode Joseph_ring(pLinkList pList,int k)
{
pLinkNode tmp = NULL;
pLinkNode pvr = NULL;
pLinkNode cur = NULL;
int m = k;
if(m <= 0)//异常输入则跳出,返回NULL;
{
printf("参数有误\n");
return NULL;
}
assert(pList);
tmp = pList->pHead ;//从链表的第一个结点,即从1开始;
while(tmp != tmp->next)
{ //当指向该结点的指针和该结点的next一样时,代表只剩一个结点,跳出;
k = m;//每次进来将k重新初始化为要删除的报数m;
while(k--)//k逐渐逼近0,则报数逐渐逼近m;
{
pvr = tmp;
tmp = tmp->next ;
}
//注意用ptr保存指向当前结点的指针;
//当报数到m时,则用下一个结点的data覆盖当前结点的data;
//删除当前结点的下一个节点;类似于删除非尾结点;
pvr->next = tmp->next ;
pvr->data = tmp->data ;
free(tmp);
tmp = NULL;
tmp = pvr;
}
return tmp;//返回最后的一个结点的指针;
}
void test()
{
//自己设计链表
LinkList List1 ;
pLinkNode tmp = NULL;
int ret = 0;
//自己测试的自定义单链表;
InitLinkList(&List1);
PushBack(&List1, 1);
PushBack(&List1, 2);
PushBack(&List1, 3);
PushBack(&List1, 4);
PushBack(&List1, 5);
PushBack(&List1, 6);
PushBack(&List1, 7);
PushBack(&List1, 8);
MakeRing(&List1);//构造环;让8指向1;
tmp = Joseph_ring(&List1,3);//找出约瑟夫问题的胜出者;
if(tmp != NULL)
printf("%d\n", tmp->data);
}
int main()
{
test();
system("pause");
return 0;
}
运行结果:(你可以自己修改 m的值和 链表的初始化,进行测试)
未完待续!