约瑟夫环—数据结构小问题
by cenyu
最近正在学习数据结构,约瑟夫环也是我的第一个小练习,节选自严蔚敏的《数据结构题集》,既能为大家提供参考,也能在这里复习一下。如有错误,请及时指出
e.g:源代码通过测试系统测验,其准确性无误。
- 问题描述
- 问题思路
- 核心思想及代码
- 源代码
- 结语
问题原文
约瑟夫(Joseph)问题的一种描述是:编号为1,2,…,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时钟方向自一开始报数,报到m时停止报数。报m的人出列,把他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直到所有人全部出列为止。试设计一个程序求出此顺序。
问题思路
本题考查的是单向循环链表的知识,由题干的“围坐一圈”以及它的出表方式可判断此为单循环链表。所谓单循环链表,较普通单链表而言,无非是首尾相连。如果设头结点,由于单循环的缘故,导致访问尾节点时需要遍历整个链表。而设尾节点的话,访问头结点就十分简单方便了,无非是tail->next即可访问(tail是尾节点)。所以本题用尾插法来构建链表。节点包括三个元素,num(原始编号),password(每个人所持的密码),next。传入password数组来一次性构建链表。每次循环到m时,读取该节点的密码作为新的m值并删除该节点,然后继续循环。直到删除完所有节点,满足循环条件为止。在实现单循环链表时,注意tail指针的位置和移动轨迹。
核心代码
for(j=0;j<n;j++){
//限定循环次数
for(i=1;i<m;i++){
p=p->next;//从当前点开始报数,经过m次
}
/*删除节点*/
dtemp=p->next;
p->next=dtemp->next;
m=dtemp->password;
printf("%d ",dtemp->num);
free(dtemp);
}
源代码
#include<stdio.h>
#include<stdlib.h>
typedef struct Node{
int num;
int password;
struct Node *next;
int dnum;
}Node;//创建节点
Node* create_loop_list(int n,int *p_word){
Node *tail=NULL,*L,*tp;
int i;
for(i=1;i<=n;i++){
L=(Node*)malloc(sizeof(Node));
if(i==1) tp=L;
L->num=i;
L->password=p_word[i];
L->dnum=i;
if(tail==NULL){
tail=L;
tail->next=L;
}//尾节点为空
else{
L->next=NULL;
tail->next=L;
tail=L;
}
}
tail->next=tp;
return tail;
}//尾插法创建单循环链表
void delete_Node(Node *tail,int m,int n){
Node *p=tail,*dtemp,*dstmp;
int i,j;
for(j=0;j<n;j++){
for(i=1;i<m;i++){
p=p->next;
}
dtemp=p->next;
p->next=dtemp->next;
m=dtemp->password;
printf("%d ",dtemp->num);
free(dtemp);
}
}
int main(){
int Password[100],m,n,amp=0,tmp,i,j=0,k=0,Number[100];
Node *Tail,*test;
scanf("%d",&n);
scanf("%d",&m);
for(i=1;i<=n;i++)
scanf("%d",&Password[i]);
Tail=create_loop_list(n,Password);
delete_Node(Tail,m,n);
return 0;
}
结语
本题难度并不高,在这里只是为了提供给大家一种比较常规的解题思路。欢迎大家提出更好的思路进行交流。