约瑟夫环问题的一般性解答

注:本代码仅供参考,如果发生学术问题博主概不负责。

题目:

N个人围成一圈,从第S个开始报数,每隔第M个将被杀掉,最后剩下X个,其余人都将被杀掉。例如N=6,S=1,M=5,被杀掉的人的序号为5,4,6,2,3,最后剩下1号。

要求:这道题博主是在学习循环链表时遇到的,所以除了循环链表,其他方法就不展示了。

设计思路:
1.创建链表
2.节点删除
3.链表遍历输出
4.释放内存

解释:采用循环链表的变式解决问题,首先通过建立一个头部为哑节点(头部不存数据)的链表,然后将链表的尾结点的指针域指向头节点的下一节点形成变形循环链表。然后从头节点的下一节点开始进行删除节点工作,删除节点操作由两部分完成,一部分是删除节点的上一节点的指针域指向删除节点的下一节点,这一步叫做摘除节点,然后用free函数释放删除节点的内存,完后删除,最后让头节点的指针域重新指向剩余节点,遍历输出链表存储的信息

#include<stdio.h>
#include<stdlib.h>
/*创建结构体,包含人的编号,名字,性别,年龄,指针域*/
typedef struct person{
	int number;
	char name[20];
	char sex[3];
	int age;
	struct person* next;
}Person;

/*调用函数*/
Person* create_head();//创建头节点
void create_linklist(Person* head, int i);//创建链表
void print_linklist(Person* head);//打印链表
void delete_node(Person* head, int n, int s, int m, int x);//删除节点
void delete_list(Person* head);//删除链表,释放内存


int main()
{
	/*
		person_n表示总人数
		person_s表示开始报数人的编号
		person_m表示间隔人数
		person_x表示剩余人数
	*/
	int person_n, person_s, person_m, person_x, flag = 0;
	Person* ListHead = NULL, *demo4 = NULL;
	ListHead = create_head();
	printf("请输入总人数:\n");
	while(true)//判断输入的总人数是否合法
	{
	scanf("%d",&person_n);
	if(person_n==0)
	printf("错误,总人数不能为0,请重新输入:\n");
	else
	break;
	}
	create_linklist(ListHead,person_n);//创建链表
	print_linklist(ListHead);//打印链表
	printf("请输入开始报数人的编号:\n");
	demo4 = ListHead->next;
	while(true)//判断开始报数的人编号是否合法
	{
		scanf("%d",&person_s);
		while(true)
		{
			if(demo4->number==person_s)
			break;
			else
			{demo4 = demo4->next;
				if(ListHead->next==demo4)
				{	flag = 1;
				    break;
				}
			}
		}
		if(flag==1)
		{
			printf("错误,编号不存在,请重新输入:\n");
			flag = 0;
			demo4 = ListHead->next;
		} 		
		else
		break;
	}
	printf("请输入报数间隔人数:\n");
	while(true)//判断间隔人数输入是否合法
	{
	scanf("%d",&person_m);
	if(person_m<=0)
	printf("间隔人数必须大于0,请重新输入:\n");
	else
	break;
	}
	printf("请输入剩余人数:\n");
	while(true)//判断剩余人数输入是否合法
	{
		scanf("%d",&person_x);
		if(person_x<0||person_x>person_n)
		printf("剩余人数必须大于0且剩余人数不能大于总人数,请重新输入:\n");
		else
		break;
	}
	delete_node(ListHead,person_n,person_s,person_m,person_x);//删除节点
	delete_list(ListHead);//释放链表内存
	return 0;
}

/*创建头节点*/
Person* create_head()
{
	Person* head = (Person*)malloc(sizeof(Person));//给头节点分配内存
	if(head == NULL)
	printf("Head is not successfully created!");//如果头节点创建失败则提示
	head->next = NULL;
	return head;
}

/*创建循环链表*/
void create_linklist(Person* head, int i)
{
	//创建尾节点,让尾结点指向头节点初始化
	Person* tail = head;
	tail->next = NULL;
	int number1 = i, flag1 = 1;
	//输入的i表示总共有多少人
	while(number1--)
	{
		Person* NewNode = (Person*)malloc(sizeof(Person));
		Person* demo0 = head->next;
		if(NewNode == NULL)
		printf("NewNode is not successfully created!");
		NewNode->next = NULL;
		/*依次输入编号,名字,性别,年龄*/
		printf("请输入编号:\n");
		if(number1 == i-1)
		scanf("%d",&NewNode->number);
		else
		{
		while(true)//判断输入编号是否重复
		{
			scanf("%d",&NewNode->number);
				while(true)
				{
					if(demo0->number != NewNode->number)
					{
						demo0 = demo0->next;
						if(!demo0)
						{flag1 = 0;
						break;
						}
					}
					else 
					break;
				}
				if(flag1==1)
				{
					printf("错误,编号已存在,请重新输入:\n");
					demo0 = head->next;
				} 		
				else
				break;
		}
		}
		printf("请输入名字:(不超过20个字符)\n");
		scanf("%s",NewNode->name);
		printf("请输入性别: (请输入nan或nv,男:nan 女:nv)\n");
		scanf("%s",NewNode->sex);
		printf("请输入年龄:\n");
		while(true)
		{
		scanf("%d",&NewNode->age);
		if(NewNode->age<0)
		printf("年龄不能为负数!请重新输入:\n");
		else
		break;
		}
		printf("\n");
		tail->next = NewNode;//尾节点指向新节点
		tail = tail->next;//指向下一个节点
	}
	tail->next = head->next;//将尾节点指向头节点下一节点,形成变形循环链表
}

/*输出各节点值*/
void print_linklist(Person* head)
{
	Person* demo2 = head->next;
	int m = 1;
	while(demo2)
	{
		printf("输入的第%d个人的信息是:\n",m);
		printf("编号:%d\n姓名:%s\n性别:%s\n年龄:%d\n\n",demo2->number,demo2->name,demo2->sex,demo2->age);
		m++;
		demo2 = demo2->next;
		if(head->next == demo2)
		break;	
	}	
}

/*删除链表节点*/
void delete_node(Person* head, int n, int s, int m, int x)
{
	Person* demo1 = head, *p = NULL, *demo2 = NULL;
	int total = n;//剩余总人数
	
	//找到开始节点,用p定位
	p = head;
	while(p->number!=s)
	{
		demo1 = p;
		p = p->next;
	}
	
	//每间隔m人,删除信息
	while(total > x)
	{
			//找到报m的人
			for(int i=1; i<m; ++i)
			{
					demo1 = p;
					p = p->next;
			}
			printf("被删除的人编号是:%d\n", p->number);
			/*有3种情况
			1.一般情况,剩余总人数大于2
			2.特殊情况,剩余总人数等于2
			3.特殊情况,剩余总人数等于1
			*/
			if(total>2)
			{
				demo1->next = p->next;//摘除节点
				free(p);//释放节点
				p = demo1->next;
			}
			else if(total==2)
			{
			    demo1->next = p->next;//摘除节点
				free(p);//释放节点
				p = demo1->next;
				p->next = NULL;//解除循环
			}
			else if(total==1)
			{
			    p = NULL;
			    demo1 = NULL;
				free(p);
				free(demo1);
			}
			total--;
	}
	head->next = p;
	if(head->next==NULL)
	printf("所有人都被删完了!");
	else
	{
	demo2 = head->next;
	while(demo2)
	{
		printf("还剩下编号为%d,姓名为%s,性别为%s,年龄为%d的幸运儿!\n",demo2->number,demo2->name,demo2->sex,demo2->age);
		demo2 = demo2->next;
 		if(head->next==demo2)//循环链表遍历输出回到起点就跳出循环
 		break;
	}
	}
}

//释放链表内存
void delete_list(Person* head)
{
	Person* demo2 = head;
	while(demo2)
	{
	head = head->next;
	free(demo2);
	demo2 = head;
	}
	return;
}
/*
	这是测试数据:
	3 0 wu nan 20 1 liu nv 19 2 wang nan 21 2 3 1
*/
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值