约瑟夫问题:
编号为1~N的N个人按顺时针方向围坐一圈,每人持有一个密码(正整数),开始任选一个正整数作为报数上限值M,从第1个人按顺时针方向自1开始顺序报数,报到M时停止报数。报M的人出列,将他的密码作为新的M值,从他顺时针方向上的下一个人开始从1报数,如此下去,直至所有人全部出列为止。
解析:
显然当有人退出圆圈后,报数的工作要从下一个人开始继续,而剩下的人仍然围成一个圆圈,因此可以使用循环单链表。退出圆圈的工作对应着表中节点的删除操作,对于这种删除操作频繁的情况,选用效率较高的链表结构,为了程序指针每次都指向一个具体的代表一个人的节点而不需要判断,链表不带头节点。因此,对于所有人围成的圆圈所对应的数据结构采用一个不带头节点的循环链表来描述。
1 #include "stdafx.h" 2 #include <iostream> 3 #include "string.h" 4 using namespace std; 5 6 typedef struct node 7 { 8 int data; 9 node *next; 10 }node; 11 12 node *create(int n) //创建节点数量为n的单向循环链表 13 { 14 node *pRet = NULL; 15 if(n != 0) 16 { 17 int n_idx = 1; 18 node *p_node = NULL; 19 p_node = new node[n]; 20 if(p_node == NULL) 21 return NULL; 22 else 23 memset(p_node,0,n*sizeof(node)); //初始化内存 24 pRet = p_node; 25 while(n_idx < n) //构造每一个节点,分别赋值1~n-1 26 { 27 p_node->data = n_idx; 28 p_node->next = p_node+1; 29 p_node = p_node->next; 30 n_idx++; 31 } 32 p_node->data = n; //构造最后一个节点 33 p_node->next = pRet;//最后一个节点的next指针指向第一个节点 34 } 35 return pRet; 36 } 37 38 int main(int argc,char *argv[]) 39 { 40 node *pList = NULL; 41 node *pIter = NULL; 42 int n = 20; 43 int m = 6; 44 pList = create(n); //构造节点数为20的循环单链表 45 //取数 46 pIter = pList; 47 m %= n; 48 while(pIter != pIter->next) 49 { 50 int i; 51 for(i=1;i<m-1;i++) //先取到第m-1个节点 52 pIter = pIter->next; 53 cout<<pIter->next->data<<"--"; //输出第m个节点的值 54 pIter->next = pIter->next->next;//删除第m个节点 55 pIter = pIter->next; 56 } 57 cout<<pIter->data<<endl; 58 delete []pList; //释放申请的空间 59 system("pause"); 60 return 0; 61 }
执行结果: