【C语言】单向循环链表(实例详解约瑟夫问题)

        类似单链表,循环链表也是一种链式的存储结构,由单链表演化而来。

        单向循环链表来源于单链表,所以单链表的基本操作基本都适合于循环链表。两者的不同点主要表现在链表的建立和链表表尾的判断。

        

        接下来我们通过分析一个经典的问题即 “ 约瑟夫问题 ”  来理解单循环链表。

        “ 约瑟夫问题 ” 简单来说就是:有n个人围成一个圈,规定一个数字num,从某个人开始报数,每次从1报到这个数字num,报到num的人出列,现在要我们按顺序输出所有人出列的顺序。

        程序分析:n个人围成一个圈就是一个循环链表,然后从将1到n分别放入每个结点的数据域中,出列就是删除结点,每出列一个就打印一个数据data。

        注意:在创建单链表时,我们将需要在链表结尾插入的结点都初始化为指向NULL,而在创建单循环链表时,需要将最后的结点初始化为指向头结点或首结点。在本题中我们将每个结点初始化为指向首结点,因为头结点的数据域中不存放数据,如果把头结点放入圈中,那就多了个空,感觉比较别扭。

        下来展示代码及注释:

#include<stdio.h>
#include<stdlib.h>
typedef struct node{
	int data;
	struct node* next;
}Node;
//创建链表-------------------------------------------------------------------------------
Node* CreatList(int n)
{
	int i;
	Node* head=(Node*)malloc(sizeof(Node));
	Node* newnode;
	Node* tail=head;
	for (i=1;i<=n;++i)
	{
		newnode=(Node*)malloc(sizeof(Node));
		newnode->data =i;//将每个人的编号依次放入链表中 
		
		tail->next =newnode;
		newnode->next =NULL;
		tail=newnode;	
	}
	//注意下面这一步与创建单向链表不一样,这里让尾结点指向首结点,形成一个没有头结点的环 
	tail->next =head->next ; 
	return head;
}
//出列就相当于删除结点--------------------------------------------------------------------
void DeletNode(Node* head,int who,int num)
{
	Node* aim;
	Node* move=head->next;
	int i;
	for (i=1;i<who;++i)       //移动到开始的那个人的位置上 
	{
		move=move->next ;
	}
	while (move->next !=move) //结束条件是链表是否只剩下一个存储有效数据的结点 
	{
		for (i=1;i<num-1;++i) //移动到出列那个人前面那个人的位置上 
		{
			move=move->next ;
		} 
		aim=move->next ;      //aim 指向出列的那个人 
		printf("%d",aim->data );
		
		move->next =aim->next;//将出列的前面那个人和出列的后面那个人连接 
		move=move->next ;
		free(aim);            //出列就意味着被移出链表,就是释放该空间 
		aim=NULL;
	}
	printf("%d\n",move->data );
	free(aim);
	move=NULL;
	free(head);
	head=NULL;
} 
int main(void)
{
	Node* h1;
	int n;   //总共有多少人 
	int who; //从第几个人开始报数 
	int num; //每次从1报到几
	      
	printf("请输入总人数:");
	while (1)
	{
		scanf("%d",&n);
		if (n<1)
			printf("请重新输入:");
		else
			break;
	}
	
	printf("请输入从第几个人开始报数:");
	while (1)
	{
		scanf("%d",&who);
		if (n<1)
			printf("请重新输入:");
		else
			break;
	}
	
	printf("请输入每次报数从1报到几:");
	while (1)
	{
		scanf("%d",&num);
		if (n<1)
			printf("请重新输入:");
		else
			break;
	}
	
	h1=CreatList(n);
	printf("出列顺序:");
	DeletNode(h1,who,num);
	return 0;
}

        运行结果: 

请输入总人数:50
请输入从第几个人开始报数:1
请输入每次报数从1报到几:6
出列顺序:6  12  18  24  30  36  42  48  4  11  19  26  33  40  47  5  14  22  31  39  49  8  17  28  38  50  10  23  35  46  13  27  43  7  25  44  15  34  3  32  9  41  21  16  2  20  37  29  1  45

  • 12
    点赞
  • 104
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不怕娜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值