约瑟夫环的数组和链表分别实现

约瑟夫环的数组和链表分别实现

问题描述

约瑟夫环问题的一种描述是:编号为1,2,…,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直到所有人全部出列为止。试设计一个程序求出出列顺序。用顺序表和链表结构分别实现。

测试数据

(1)n=7,7个人的密码依次为:3,1,7,2,4,8,4,首先m值为6(正确的出列顺序应为6,1,4,7,2,3,5)。
(2)n=9,9个人的密码依次为:4,3,7,1,5,6,3,8,2,首先m值为4
(3)自己任选其它一组测试数据。

代码

顺序表结构

主要是用索引%人数m得出真实的位置

#include <stdio.h>
#include <malloc.h>

#define MAXSIZE  100
typedef struct Cir *list;
struct Cir{
	int pw[MAXSIZE];        //存储密码 
	int isdeq[MAXSIZE];     //是否出队 0不出队,1出队 
	int Last;               //表示表尾的索引  
};

void init(list &l){
	l = (list)malloc(sizeof(struct Cir));
	l -> Last = -1;
}

void append(int X, list &l){
	if(l -> Last == MAXSIZE-1){
		printf("表满");
		return;
	}
	l->pw[l->Last+1]=X;
	l->isdeq[l->Last+1]=0;  //出队全部初始为0 
	l->Last++;              //表长加一 
	return;
} 

void add(int n, list &l){
	init(l);
	l = (list)malloc(n*sizeof(struct Cir));
	l -> Last = -1;
}

int main(){
	list l;	
	int n;
	printf("输入游戏人数n:");
	scanf("%d",&n);
	//动态扩充顺序表容量 
	add(n,l);
	
	printf("输入%d个人的密码:",n);
	int i;
	for(i=0; i<n; i++){
		int X;
		scanf("%d",&X);
		append(X,l);
	}
	
	int m;
	printf("输入开始第一个报数值m:");
	scanf("%d",&m);
	
	int index=-1;
	printf("出列顺序:");
	while(l->Last >= 0){
		while(m > 0){
			index += 1; 
			index %= n;
			if(l->isdeq[index] == 1){
				continue;
			} 
			if(m ==1){
				printf("%d ",index+1);
				l->isdeq[index] = 1;
				m = l->pw[index];
				l->Last--;
				break;
			}								
			m--;
		}						 		
	} 
	free(l);
	return 0;
}

运行截图
在这里插入图片描述

链表结构

使用指针遍历单向循环链表,每当报数到m值时,删除当前指针节点,更改m值为删除人的密码,修改指针的为删除节点的前一个节点。

#include <stdio.h> 
#include <malloc.h>

typedef struct Lnode *list;
struct Lnode{
	int num;			  //存储序号 
	int data;             //存储密码 
	struct Lnode *next;   //下一跳的节点指针          
};

//创建空链表  只有头指针 
list init(){
	list head = (list)malloc(sizeof(struct Lnode));
	head ->next =NULL;
	return head;
}

//添加节点方法 
list add(list &l,int num){
	//创建头结点 
	list head = init();
	//创建指针,指向头结点 
	list flag = head;
	int i;
	//往头结点后插入num个节点 
	for(i=0;i<num;i++){
		flag->next = (list)malloc(sizeof(struct Lnode));
		//创建节点成为头结点的下一跳	
		flag = flag->next;
		//给节点编序号,并输入密码赋值给data 
		flag->num = i+1;
		scanf("%d",&flag->data);
	}
	//尾节点的下一跳指向头结点下一跳,形成循环链表		
	flag ->next =  head->next;
	//返回头结点 
	return head;
}


list deleteNode(list &lbefore,int &m){
	//创建p指针删除节点的前一个节点,q指向删除节点 
	list p,q;
	p=lbefore;
	//找到要删除节点的前一项节点 
	while(m>1){
		p = p->next;
		m--;
	}
	//修改p->q->next  改为 P->next   	
	q = p ->next;
	p->next = q->next;
	//输出删除节点的序号,更改m值为删除节点的密码 ,释放删除节点 
	printf("%d ",q->num);
	m = q ->data;
	free(q);
	lbefore = p;
	//返回删除节点的前一个节点 
	return lbefore; 
}

game(list &l,int m,int n){
	//当人数n等于1时游戏结束 
	while(n-1){
		//删除第m个节点, 传入l是上一个删除节点的前一个节点 
		deleteNode(l,m);
		n--;
	}
	//输出最后一个人的密码 
	printf("%d",l->num);
}


int main(){
	list l;
	
	int n;
	printf("输入游戏人数n:");
	scanf("%d",&n);
	
	printf("输入%d个人的密码:",n);
	int i;
	//插入n个节点和密码 
	l=add(l,n);				
	
	int m;
	printf("输入游戏开始的第一个报数值m:");
	scanf("%d",&m);
	
	//开始游戏  
	game(l,m,n);
	return 0;
}

运行截图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值