问题描述:
1-n个人按顺时针方向围坐在圆桌周围 ,每个人持有一个密码(正整数)
一开始任选一个正整数作为报数上限值m,从第一个人开始顺时针从1开始报数直到m停止
报m的人出列,将他的密码作为新的m值,从他的顺时针方向下一个人开始从1报数
报到m出列,循环下去,直到所有人出列。
算法逻辑:
建立n个结点(无头结点)的单循环链表
从链表的第一个结点起循环计数,寻找第m个节点
找到后将其password作为新的m值,并从链表中删除该结点
循环,直到链表为空
注意事项:
!!ATTENTION!! 函数形参只能值传递,如果用指针做形参开辟内存,或者指针本身作为要修改的值,则注意用指针的指针
!!ATTENTION!! 注意循环执行的条件,如果还想要多执行几步:
要么中间设置一个 条件语句和变量,作为返回值进行判断;
要么最后再重复几步。
//自定义结构体类型(结点)
typedef struct NodeType {
int id;
int password;
struct NodeType * next;
} NodeType;
//函数声明
void CreateList (NodeType **, int);
int IsEmptyList (NodeType *);
void JosephusOperate (NodeType **, int);
void PrintList (NodeType *);
int main(){
int n = 0;
int m = 0;
NodeType * p = NULL;
printf("请输入人数 n = ");
scanf("%d",&n);
printf("请输入初始密码 m = ");
scanf("%d",&m);
CreateList (&p, n);
printf("-------打印链表如下-------\n");
PrintList (p);
printf("-------依次出队顺序如下-------\n");
JosephusOperate (&p, m);
}
//创建单循环链表
void CreateList( NodeType **pHead, int n) {
int i = 1; //n个节点,从i开始循环录入
int ipassword = 0; //循环录入的每个人的密码
NodeType *pNew = NULL;
NodeType *pCurr = NULL;
for ( i=1; i<=n; i++) {
//开辟一个结点(第一次是首元结点)
printf("请输入第%d个人的初始密码:",i);
scanf("%d", &ipassword);
pNew = (NodeType *) malloc (sizeof (NodeType));
printf("i=%d, addre=%d\n", i, pNew);
if (!pNew){
printf ("Error");
exit (ERROR);
}
pNew -> id = i;
pNew -> password = ipassword;
pNew -> next = NULL;
if (*pHead == NULL) {
*pHead = pCurr = pNew ;
pCurr -> next = *pHead; //next域指向pHead,形成循环链表
}
else { //尾插法:
pNew -> next = pCurr -> next; //新结点pNew的next域指向pHead
pCurr -> next = pNew; //pCurr指向新结点pNew,pNew链入表中
pCurr = pNew; //pCurr作为动态标尺,后移一位
}
printf("phead=%d\n", *pHead);
}
printf("完成单向循环链表的创建!\n");
}
//打印链表看是否成功创建了
void PrintList (NodeType *ppHead) {
NodeType * pCurrent = ppHead;
if (!IsEmptyList(ppHead)) {
printf("--ID----PASSWORD----\n");
do{
printf("%4d %7d\n",pCurrent->id, pCurrent->password);
pCurrent = pCurrent -> next;
} while(pCurrent != ppHead);
}
}
//判断链表是否为空
int IsEmptyList (NodeType *Head) {
if(Head)
return 0;
else {
printf("The list is empty!\n");
return 1;
}
}
//约瑟夫环运行函数(打印、删除节点,再依次计数)
void JosephusOperate(NodeType **pHead, int ipassword) {
int j = 0;
NodeType * pPre = *pHead;
NodeType * pCur = *pHead;
NodeType * pDel = NULL;
int k = 1;
//切记,有可能第一个m==1,则第一个人站起来就出列,即第一个结点就需要删除
//所以pCur应指向第一个结点,pPre指向尾结点
while (pPre -> next != *pHead)
pPre = pPre -> next;
while (k){
for (j=1; j < ipassword; j++) {
pPre = pCur;
pCur = pCur -> next;
} //找到密码对应的人
if (pPre == pCur) k=0;
ipassword = pCur -> password;
printf("第 %d 个人出列,他的密码是:%d\n", pCur->id, pCur->password);
pPre -> next = pCur -> next;
pDel = pCur;
pCur = pCur -> next;
free (pDel);
}
*pHead = NULL;
}