约瑟夫环问题(两种方式实现)

目录

1.用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。

(1)数组形式

 (2)循环链表


1.用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。

(1)数组形式

//1.数组方式
/*
* 约瑟夫环思路:
* 1.假设有n个人,数到m的人出列,因为是循环数数,没有必要m要小,比如5个数,那m也可以是6.
* 2.从1开始给n个人编号
* 2.定义Num,从1开始,数到m,num==m时,此时这个数据输出并从数组中删除,之后又从1开始数
* 3.每次检验到的数据要先保存,再拿出数组,检验过后,可选择是否放回数组,如果不是该退出的数据,就要补在当前数组的最后边,比如数组1 2 3 4 5 ,先拿出1,发现不是该放出去的数据,再次放回时,数组就变成了2 3 4 5 1
  4.由3可知每次移出数组的数同时,后面的数要通通前移,被检验的数可选择放在最后,或者出数组。
  5.关于循环,每次退出数据的数据那数组长度就要--,当数组长度为0时,便结束循环,输出最后留下的数。

*/
int  JosephRing(int* arr, int m, int n)//n是总人数,m是数到m的人出列
{
	
	for (int i = 1; i <= n; i++)//给n个人编号,从1开始
	{
		arr[i] = i;
	}
     int num = 0;
	 int index = 1;//被检验数据的下标
	 int temp = 0;
	 while (n)
	 {
		 num++;//从1开始数到m,叫到m的数出数组
		 temp = arr[index];//临时变量保存被检验的数
		 arr[index] = 0;//待检验的数移出数组
		 for (int i = index + 1; i <= n; i++)
		 {
			 arr[i-1] = arr[i];
			 //arr[i]=arr[i+1];//如果是这样往前移动的话,i第一趟是从2开始的,那下标是1的位置就没有数,而且会吧2下标原来元素覆盖掉
		 }
		 if (num == m)//数到要退出的数
		 {
			 num = 0;
			 n--;
			// printf("%5d", temp);
			 continue;//如果数到要退出的数,那不必执行 arr[n] = temp,continue关键字会结束此时的循环,开始下一轮循环,不需要往下执行。
		 }
		 arr[n] = temp;//被移出的数放在最后边
	 }
	 return temp;

}

int main()
{
	int arr[256];
	int m, n;
	scanf_s("%d %d", &n,&m);
	int res = JosephRing(arr, m, n);
	printf("留下的人是:%d\n", res);
	
}

截图:

 (2)循环链表

struct node
{
	int data;//数据域
	struct node* next;// 指针域
};

int main()
{
	int m, n;//n个人,报到m的人出列
	int answer[100];//存储答案的数组
	int i;//从1到m循环数数
	int count=0;//控制答案的下标
	struct node * head, * tail, * p, * q;//p指向当前节点,q是p的下一节点

	//初始化链表
	//1.申请节点空间
	head = (struct node*)malloc(sizeof(struct node));//注意malloc的用法
	//指针域不使用,但为了调式方便,值定为-1
	//head->data = -1;
	head->next = NULL;//只申请一个节点,指针域指向空
 
	//循环处理多组数据
	while (1)
	{
		scanf_s("%d %d", &n, &m);
		if (m == 0 || n == 0)//输入0 0 时,代表结束输出
		{
			free(head);//释放节点的空间,
			break;
		}
		else
		{
			//从1开始给n个人编号
			tail = head;//最初只有一个节点
			for (i = 0; i < n; i++)
			{
				//尾插法编号,tail指向最后的节点
				//1.申请节点
				p = (struct node*)malloc(sizeof(struct node));
				//2.填数据
				p->data = i + 1;
				//3.插到尾部
				tail->next = p;
				//4.前后链接
				p->next = head->next;
				tail = p;//tail移动到最后一个节点,
			}
			p = head->next;//p是第一个节点
			q = tail;//p在最前面,最后一个节点是第一个节点的前继节点
			i = 1;//准备开始报数

		//删除操作
			while (p != q)//q一直在p的后面,一旦相等时,说明只剩一个节点
			{
				if (i == m)
				{
					//跨越式删除
					q->next = q->next->next;
					free(p);
					//p移向下一个有效节点
					p = q->next;
					i = 1;//重新开始下一轮报数
				}
				else//正常通过的数据
				{
					q = p;
					p = p->next;
					//继续数下一个数
					i++;
				}
			}
			head->next = q;//当只剩下两个节点的时候,再次删除会造成链表不完整,在这个程序中,不是必要的
			answer[count] = p->data;//保存余下的数
			count++;
			free(p);
			head->next = NULL;//链表清空
		}
	}
	for (i = 0; i < count; i++)
	{
		printf("%2d", answer[i]);
	}
	free(head);
	return 0;
}

截图:

 要常常写代码呀,不然真的会忘,加油吧!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我在凌晨等太阳¤

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

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

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

打赏作者

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

抵扣说明:

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

余额充值