约瑟夫环问题

问题描述:有n只猴子,按顺时针方向围成一圈选大王(编号从1到n),从1号开始报数,一直数到m,数到m的猴子退出圈外,剩下的猴子再接着从1开始报数。就这样,直到圈内只剩下1只猴子时,这个猴子就是猴王,编程要求输入n,m后,输出最后猴王的编号。

输入:每行连续的两个整数n,m,用一个空格分开,以0 0结束输入

输出:每行一个整数,代表猴王的编号


一.链表法

 它这个环,正好能用上循环链表

所以用到的是链表的创建和链表的删除,然后这里用到的是尾插法(插入与原节点顺序相同)

 

/*构建循环链表*/
rear = head; //每次都要先让rear先指向head
	for (int i = 0; i < n; i++) { //循环链表的创建
	Link node;
	node = (Link)malloc(sizeof(Node));
	node->next = head->next;
	node->data = i + 1; //猴子的编号
	rear->next = node;
	rear = node;
			}

关键的几步: 

tail->next = p;
p->next = head->next;
tail = p;

图解: 

 

 

利用尾插法把所有的结点插入,形成一个循环链表

 删除结点的操作

要引入两个结点,p表示当前结点,q表示p结点的前一个结点 

初始化p,q 

p = head->next;	
q = tail; //rear->next就是head->next,即q

 图解:

 

如果找到了猴子大王,我们进行删除操作

	if (k == m) { //删除该猴子
			    
		p->next = q->next;
		free(q);
		k = 1;
		q = p->next;
		m=q->password;
    }

 

 

 如果没找到

else {
		p = q;
		q = q->next;
		k++;
	}

图解: 

易错点:

1. 因为连续的输入,所以每次开始创建新链表的时候都要定义rear=head;

2. 删除到只剩下两个有效结点的时候,如果删除第一个结点会使链表不完整连贯,所以结尾要加上head->next=p,把链表连起来

3.记得free(p)最后一个猴王的链表结点,还有头指针free(head);

#include <stdio.h>
#include <stdlib.h>
 
typedef struct node {
	int data;
	struct node *next;
} Node, *Link;
 
int main() {
	int n, m;
	int answer[100];//储存猴王的编号
	int count = 0;
	Link head, rear; //尾插法做准备
	Link p, q;
	head = (Link)malloc(sizeof(Node));
	head->next = NULL;
	rear = head;
	while (1) { //保证连续的输入数据
		scanf("%d %d", &n, &m);
		if (n == 0 || m == 0) {
			free(head);
			break;//循环退出的条件
		} else {
			rear = head; //每次都要先让rear先指向head
			for (int i = 0; i < n; i++) { //循环链表的创建
				Link node;
				node = (Link)malloc(sizeof(Node));
				node->next = head->next;
				node->data = i + 1; //猴子的编号
				rear->next = node;
				rear = node;
			}
			int k = 1; //记录猴子的报数
			q = rear; //rear->next就是head->next,即q
			p = head->next;
			while (p != q) { //只剩下一个序号的时候,p,q指针重合
				if (k == m) { //删除该猴子
					q->next = p->next;
					free(p);
					k = 1;
					p = q->next;
				}//移动q到下一个有效结点上
				else {
					q = p;
					p = p->next;
					k++;
				}
			}
		}
		//删除到只剩下两个有效结点的时候,如果删除第一个结点会使链表不完整连贯
		head->next = p;
		answer[count] = p->data;
		free(p);
		count++;
	}
	for (int i = 0; i < count; i++) {
		printf("%d\n", answer[i]);
	}
	free(head);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值