单链表面试题系列之约瑟夫环

本文是单链表面试题系列的一部分,重点讨论约瑟夫环问题。通过对约瑟夫环的深入剖析,帮助读者理解和掌握解决此类面试题的关键技巧。
摘要由CSDN通过智能技术生成

***单链表面试题系列之约瑟夫环***


约瑟夫环


约瑟夫环( 约瑟夫问题 )是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围,从编号为1的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。通常解决这类问题时我们把编号从1~n,最后结果即为原问题的解。


关于约瑟夫环的故事由来,大家可以百度了解一下,很有意思的数学问题!


我在本篇博客中是从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的值和 链表的初始化,进行测试)


未完待续!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值