线性表应用三:约瑟夫环问题
任务描述
约瑟夫环是典型的线性表,可以使用不带头结点的循环单链表进行存储。本关任务,通过使用不带头结点的循环电链表存储约瑟夫环,并模拟约瑟夫环的报数出圈过程,得出约瑟夫环出圈序列。输入n(1<=n<=100)为约瑟夫初始结点个数,输入m(1<=m<=100)为一轮报数数值,输出出圈序列。 例:n=9 m=3
相关知识
据说著名犹太历史学家约瑟夫(Josephus)有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与约瑟夫及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而约瑟夫和他的朋友并不想遵从。约瑟夫要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。约瑟夫问题可以抽象为:n 个人围成一圈 (3<=n<=100)(从零开始编号),从第一个人开始报数,第m个将被杀掉 (1<=m<=n),最后剩下一个,其余人都将被杀掉 。例如n = 6,m = 5,被杀掉的顺序为:4, 3, 5, 1, 2, 0。
编程要求
本关的编程任务是补全step8/polynomailList.h
文件中的 createJoselink ()、move ()、deleteNextnode ()、printJoseque()
函数,分别实现约瑟夫环的创建,指针固定步数的移动、后继结点的删除、约瑟夫环删除序列打印,这四个函数的具体说明如下:
// 函数createJoselink:创建包含n个结点(不带头结点)的循环单链,结点的数据值以此是1~n // 参数:n-约瑟夫环初始结点个数 // 返回值:约瑟夫环不带头循环单链表的首个结点指针 node* createJoselink(int t);
// 函数move:指针向前移动step步 // 参数:p-起点指针指向的结点位置,step-指针在循环单链中向前移动的步数 // 返回值:从p指针所指向的结点位置,向前移动step步后所指向的结点位置 node* move(node* p, int step);
// 函数deleteNextnode:删除p指针所指向的下一个结点 // 参数:pre-指向待删除结点的前驱结点(注意:当循环单链只剩下一个结点时,pre指针指向的结点即待删除结点) void deleteNextnode(node* pre);
// 函数printJoseque:输出约瑟夫环删除序列,编号之间用一个空格隔开 // 参数:n-约瑟夫环初始总人数,m-一轮报数值 void printJoseque(int n, int m);
评测说明
本关中包含两个文件分别是: step8/joseLink.h :此文件为学员文件,包含不带头结点的循环单链表结构约瑟夫环问题相关处理函数,其中createJoselink () 、move () deleteNextnode ()、printJoseque()为学员待实现函数。 step8/test.cpp:此文件为评测文件(含main函数),引用“joseLink.h”,主要用于输入约瑟夫环初始人数n和一轮报数数值m,并调用printJoseque()函数输出约瑟夫环删除序列。 (上述两个文件可通过点击在代码取的右上角文件夹中的step8文件夹中查看)
输入输出说明
输入约瑟夫环初始人数n(1<=n<=100),输入一轮报数数值m(1<=m<=100),输出约瑟夫环元素删除序列,例如:
测试输入: 6 5 预测输出: 5 4 6 2 3 1
测试输入: 9 3 预测输出: 3 6 9 4 8 5 2 7 1
test.h
#include "joseLink.h" int main() { int n, m; // 输入初始总人数n,和一轮报数值m cin >> n >> m; // 输出约瑟夫出圈序列,两编号之间用一个空格隔开 printJoseque(n, m); return 0; }
joseLink.h
#include <iostream> using namespace std; //单链表结点结构定义 struct node { int data; node* next; // 指针域,指向下一个结点 }; // 函数createJoselink:创建包含n个结点(不带头结点)的循环单链,结点的数据值以此是1~n // 参数:n-约瑟夫环初始结点个数 // 返回值:约瑟夫环不带头循环单链表的首个结点指针 node* createJoselink(int t); // 函数move:指针向前移动step步 // 参数:p-起点指针指向的结点位置,step-指针在循环单链中向前移动的步数 // 返回值:从p指针所指向的结点位置,向前移动step步后所指向的结点位置 node* move(node* p, int step); // 函数deleteNextnode:删除p指针所指向的下一个结点 // 参数:pre-指向待删除结点的前驱结点(注意:当循环单链只剩下一个结点时,pre指针指向的结点即待删除结点) void deleteNextnode(node* pre); // 函数printJoseque:输出约瑟夫环删除序列,编号之间用一个空格隔开 // 参数:n-约瑟夫环初始总人数,m-一轮报数值 void printJoseque(int n, int m); // 函数delList:删除链表,释放空间 // 参数:h-链表头指针 void delList(node* h); // 函数printList:输出链表,每个数据之间用一个空格隔开 // 参数:h-链表头指针 //void printList(node* h); void delList(node* h) { node* p = h; //指针p指向头结点,第一个要删除的结点 while (p) //这个结点是存在的 { h = h->next; //头指针h指向下一个结点(下一个结点的地址存在当前结点的指针域中,即h->next中 delete p; //删除p指向的结点 p = h; //p指向当前的头结点,即下一个要删除的结点 } } node *newNode(int data) { node *temp = new node; temp->next = temp; temp->data = data; } node* createJoselink( int t) { // 请在此添加代码,补全函数createJoselink /********** Begin *********/ node *p, *q; p = (node*)malloc(sizeof(node)); p->next = NULL; p->next = p; for (int i = 1; i <= t; i++) { q = (node*)malloc(sizeof(node)); q->data = i; q->next = p->next; p->next = q; } return p; /********** End **********/ } node* move(node* p, int step) { // 请在此添加代码,补全函数move /********** Begin *********/ for(int i = 0; i < step ; i++) //移动到删除节点位置 { p = p->next; } return p; /********** End **********/ } void deleteNextnode(node* pre) { // 请在此添加代码,补全函数deleteNextnode /********** Begin *********/ node *q = pre->next; pre->next = q->next; free(q); /********** End **********/ } void printJoseque(int n, int m) { // 请在此添加代码,补全函数printJoseque /********** Begin *********/ node *head = newNode(1); node *prev = head; for (int i = 2; i <= n; i++) { prev->next = newNode(i); prev = prev->next; } prev->next = head; // Connect last // node to first /* while only one node is left in the linked list*/ node *ptr1 = head, *ptr2 = head; while (ptr1->next != ptr1) { // Find m-th node int count = 1; while (count != m) { ptr2 = ptr1; ptr1 = ptr1->next; count++; } /* Remove the m-th node */ ptr2->next = ptr1->next; printf ("%d ", ptr1->data); free(ptr1); ptr1 = ptr2->next; } printf ("%d ", ptr1->data); /********** End **********/ }