循环链表最著名的应用就是Joseph问题:
Josephu问题:据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人找到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。 如何用循环链表来求解Josephu问题?
View Code
1 #include <iostream> 2 using namespace std; 3 4 typedef struct _node{ 5 int data; 6 struct _node *next; 7 }node, *pointer, *linklist; 8 9 linklist Create_clist(int n) //创建循环链表 10 { 11 pointer head,rear; 12 head=new node; 13 rear=head; 14 for(int i=1;i<=n;i++) 15 { 16 pointer s=new node; 17 s->data=i; 18 rear->next=s; 19 rear=s; 20 } 21 rear->next=head; 22 return rear; 23 } 24 25 void Joseph(linklist clist,int k,int m) //clist:循环链表,k:开始位置,m:步长 26 { 27 clist->next=clist->next->next; 28 pointer cur=clist,pre; 29 for(int i=1;i<=k;i++) 30 { 31 pre=cur; 32 cur=cur->next; 33 } 34 while(cur!=cur->next) 35 { 36 for(int i=1;i<m;i++) 37 { 38 pre=cur; 39 cur=cur->next; 40 } 41 cout<<"Kill "<<cur->data<<endl; 42 pre->next=cur->next; 43 delete cur; 44 cur=pre->next; 45 } 46 cout<<cur->data<<" survived."<<endl; 47 } 48 49 int main() 50 { 51 linklist clist=Create_clist(41); 52 Joseph(clist,1,3); 53 54 system("pause"); 55 return 0; 56 }
可以看出,Joseph还是给自己留了一手。
ps:招聘时,经常会遇到遇到类似Joseph问题的笔试题,比如国内某视频网站2012.9的笔试题:
有一个单向循环链表队列,从头开始报数,当报到m或者m的倍数的元素出列,根据出列的先后顺序重新组成单向循环链表。函数原型: void reorder(Node **head , int m)
下面给出实现:
View Code
1 #include <iostream> 2 #include <iomanip> 3 using namespace std; 4 5 typedef struct _node{ 6 int data; 7 struct _node *next; 8 }node, *pointer, *linklist; 9 10 linklist Create_clist(int n) 11 { 12 pointer head,rear; 13 head=new node; 14 rear=head; 15 for(int i=1;i<=n;i++) 16 { 17 pointer s=new node; 18 s->data=i; 19 rear->next=s; 20 rear=s; 21 } 22 rear->next=head; 23 return rear; 24 } 25 26 void print(linklist clist) 27 { 28 pointer head=clist->next; 29 pointer p=head; 30 while((p=p->next)!=head) 31 cout<<setw(4)<<p->data<<" "; 32 cout<<endl; 33 } 34 35 void reorder(linklist *p,int m) 36 { 37 pointer head,rear,cur,pre; 38 rear=*p; 39 head=rear->next; //头结点 40 cur=head->next; //cur指向第一个结点 41 rear->next=cur; //为了方便,头结点不参加循环 42 43 rear=head; 44 while(cur->next!=cur){ 45 for(int i=1;i<m;i++) 46 { 47 pre=cur; 48 cur=cur->next; 49 } 50 51 //出列的结点,按照尾插法重新插入链表 52 rear->next=cur; 53 rear=cur; 54 55 //跳过出列的结点,继续循环 56 cur=cur->next; 57 pre->next=cur; 58 } 59 rear->next=cur; 60 rear=cur; 61 rear->next=head; 62 63 *p=rear; 64 } 65 66 int main() 67 { 68 linklist clist=Create_clist(41); 69 reorder(&clist,3); 70 print(clist); 71 72 system("pause"); 73 return 0; 74 }
ps: Joseph问题也有相应的数学解法
1 int ret=0; 2 for(int i=2;i<n;i++) 3 ret=(ret+m)%i;