先说一下什么是约瑟夫环问题,这是百科的解释:
约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
思路:
因为n个人不定,所以采用链表。因为是连续报数的,所以是用循环链表。每数到m,那个m号的人就删除掉,然后从他身后一位重新开始,再数到m,再删除,一直这样。怎么样才能停止呢?就是当一开始的队伍没人的时候。就是一开始按顺序建立一个有顺序的链表1~n,再根据m删除,删除掉的人又重新组成一个链表,这就是出局的顺序。怎么根据m来删除呢?当从1号开始数,经过m-1个人就是要删除的那位。就是2,3,...m-1。再从m-1的后一位开始当作1,再经过m-1个人这样重复。
下面我给出我写的代码:
/* 关于我写的函数说明一下 * head,root变量分别代表一开始按顺序的队伍和出局顺序的队伍 * 插入函数中因为两个表的形成方式不同所以插入方式有所不同 * 销毁函数也是一样 * 约瑟夫问题的解决主要体现是在删除函数上 */ #include <stdio.h> #include <stdlib.h> typedef struct Node { int num; struct Node *next; }Node; /* 初始化链表 */ int InitList(Node **head, int n); /* 添加至链表,要做成循环链表 */ int InsertList(Node *head, int n, int flag); /* flag代表不同插入方式 */ /* 删除 */ int DeleteList(Node *head, Node *root, int m); /* 输出链表 */ int DisplayList(Node *head); /* 销毁链表 */ int DestroyList(Node *head, int flag); /* 代表不同销毁方式 */ /* 转置函数 */ int Transpose(Node *head); int main(void) { Node *head = NULL; /* 一开始存储按顺序的队伍 */ Node *root = NULL; /* 最后退出顺序的队伍 */ int m = 0; /* 指定退出的号码 */ int n = 0; /* 游戏的人数 */ int flag = 0; /* 用来表示在插入函数时0代表head, 1代表root */ printf("请输入人数和退出的号码: "); scanf("%d %d", &n, &m); /* 初始化 */ InitList(&head, n); /* 这个是一开始按顺序派对的队伍 */ InitList(&root, n); /* 这个是到最后储存退出顺序的队伍 */ /* 添加 */ InsertList(head, n, flag); /* 输出 */ printf("按顺序排队: "); DisplayList(head); /* 根据指定的号码m来删除 */ DeleteList(head, root, m); Transpose(root); /* 因为在删除函数中以头插法生成,所以需要倒置 */ /* 输出 */ printf("退出完毕!现在输出游戏退出顺序: "); DisplayList(root); /* 销毁链表 */ DestroyList(head, flag); flag = 1; DestroyList(root, flag); return 0; } /* 初始化链表 */ int InitList(Node **head, int n) { (*head) = (Node *)malloc(sizeof(Node)); (*head)->num = n; /* 头结点的num变量就储存人数n */ (*head)->next = NULL; return 1; } /* 添加至链表,要做成循环链表 */ int InsertList(Node *head, int n, int flag) { Node *rear = head; Node *current = head; Node *new = NULL; int i = 0; /* 这是head的插入方式,尾插法 */ if (flag == 0) { for (i = 1; i <= n; i++) { new = (Node *)malloc(sizeof(Node)); if (new == NULL) { perror("Malloc error"); exit(1); } else { new->num = i; rear->next = new; rear = new; } rear->next = NULL; } rear->next = head->next; /* 形成循环链表 */ } /* 这是root的插入方式,头插法,可以不用循环链表,因为游戏已经结束 */ else { new = (Node *)malloc(sizeof(Node)); if (new == NULL) { perror("Malloc error"); exit(1); } else { new->num = n; new->next = current->next; current->next = new; } } return 1; } /* 删除 */ int DeleteList(Node *head, Node *root, int m) { Node *current = head->next; Node *previous = NULL; int i = 0; int count = head->num; int flag = 1; while (count != 0) /* 此时队伍没人,就退出 */ { while (1) { /* 经过m-1个之后current指针就会指向要退出的元素 */ if (i == m-1) { i = 0; /* 重新开始计数 */ break; } previous = current; current = current->next; i++; } previous->next = current->next; /* 删除元素 */ InsertList(root, current->num, flag); /* 将删除出来的元素添加至新的链表中 */ free(current); count--; /* 每free一次就减少一个人 */ current = previous->next; /* 从被删掉元素的后一位重新开始 */ } return 1; } /* 输出链表 */ int DisplayList(Node *head) { Node *current = head->next; int count = head->num; int i = 1; while (1) { if (i != count) printf("%d->", current->num); else { printf("%d ", current->num); break; } current = current->next; i++; } return 1; } /* 销毁链表 */ int DestroyList(Node *head, int flag) { Node *current = head; Node *previous = NULL; int count = head->num; int i = 1; if(flag == 0) free(head); else while (current != NULL) { previous = current; current = current->next; free(previous); } return 1; } /* 转置函数 */ int Transpose(Node *head) { Node *p1, *p2, *p3 = NULL; p1 = head->next; p2 = p1->next; while (p2 != NULL) { p3 = p2->next; p2->next = p1; p1 = p2; p2 = p3; } head->next->next = NULL; head->next = p1; return 1; }
现在给出程序运行的结果:
因为是新手,所以不懂得怎么优化代码,请多多包含。