用循环链表求解约瑟夫问题

 用循环链表求解约瑟夫问题  
   
  一、问题陈述  
  一个旅行社要从n个旅客中选出一名旅客,为他提供免费的环球旅行服务。旅行社安排这些旅客围成一个圆圈,从帽子中取出一张纸条,用上面写的正整数m(  用面向过程的编程方式(C),对某个给定的n=8与m=3,给出被淘汰出列的旅客编号,以及最终的幸存者。  
  用面向对象的编程风格(C++),重新处理该约瑟夫问题。  
  谈谈这两种编程风格的优点。  
  二、用C语言解约瑟夫问题  
  1、单链表的创建与输出    
  #include  
  #include  
  #define   NULL   0  
  struct   node{   /*定义结构体*/  
  int   data;  
  struct   node   *next;  
  };  
  typedef   struct   node   NODE;/*将该结构体设置成自定义类型*/  
  NODE   *head;/*定义一个指向该结构体的头指针*/  
  NODE   *create(int   n)/*创建具有n个结点的单链表*/  
  {  
  NODE   *p;  
  int   i=1;  
  head=(NODE   *)malloc(sizeof(NODE));  
  head->next=NULL;  
  while(i<=n)  
  {  
  p=(NODE   *)malloc(sizeof(NODE));  
  p->data=n+1-i;  
  p->next=head->next;  
  head->next=p;  
  i++;  
  }  
  return(head);  
  }  
  void   output(NODE   *point)/*输出该链表数据域内的值*/  
  {  
  NODE   *p;  
  p=point->next;  
  while(p!=NULL)  
  {  
  printf("%d   ",p->data);  
  p=p->next;  
  }  
  printf("/n");  
  }  
  如果我们写一段main()函数  
  void   main()  
  {    
  head=create(8);  
  output(head);  
  }  
  便可以完成创建和输出单链表的工作。  
  如果将上述创建单链表与输出单链表的工作保存为头文件link1.h,那么在今后需要创建输出类似的单链表时,只需写以下主函数即可。  
  #inlucde   “link1.h”  
  void   main()  
  {    
  head=create(8);  
  output(head);  
  }  
  2、循环单向链表的创建与输出  
  明白了带头指针的单向链表的创建与输出,只需作简单修改便可处理循环单向链表的相关问题。这里我们建立一个新的头文件link2.h,它包含以下几段代码。  
  #include  
  #include  
  struct   node{  
  int   data;  
  struct   node   *next;  
  };  
  typedef   struct   node   NODE;  
  NODE   *head;  
  NODE   *create(int   n)  
  {  
  NODE   *p;  
  int   i=1;  
  p=head=(NODE   *)malloc(sizeof(NODE));  
  head->next=head;/*造循环链表时头指针的指针域设置*/  
  while(i<=n)  
  {  
  p->data=n+1-i;  
  p->next=head->next;  
  head->next=p;  
  i++;  
  p=(NODE   *)malloc(sizeof(NODE));  
  }  
  return(head);  
  }  
  void   output(NODE   *point,int   n)   /*n表示欲输出多少个结点,由于该链表是循环的,可输出无穷项*/  
  {  
  NODE   *p;  
  int   i=1;  
  p=point->next;  
  while(i<=n)  
  {  
  printf("%d   ",p->data);  
  p=p->next;  
  i++;  
  }  
  printf("/n");  
  }  
  3、在循环链表中删除结点并输出被删结点的相关信息  
  在头文件link2.h中增添新函数del(int   n,int   m),这里的形参n代表起始结点,m代表报数值。  
  void   del(int   n,int   m)  
  {  
  int   i;  
  NODE   *p,*q;  
  p=head;  
  /*将指针移到起始结点,即第n个结点*/    
  i=0;  
  while(i  {  
  p=p->next;  
  i++;  
  }  
  /*删除满足报数值的结点*/    
  while(p->next!=p)  
  {  
  i=1;  
  while(i  {  
  p=p->next;  
  i++;  
  }  
  /*先输出,后删除*/  
  q=p->next;  
  printf("%d   ",q->data);  
  p->next=q->next;  
  free(q);  
  }  
  printf("/nonly   one   %d",p->data);/*输出仅剩的结点*/  
  }  
  4、解决约瑟夫问题的主函数  
  #include    
  void   main()  
  {  
  /*number结点个数,item输出结点的个数,location报数的起始位置,callnum报数值*/  
  int   number,item,location,callnum;  
  printf("/ninput   nose   number=");  
  scanf("%d",&number);  
  printf("/noutput   item=");  
  scanf("%d",&item);  
  head=create(number);  
  output(head,item);  
  printf("/ninput   location=");  
  scanf("%d",&location);  
  printf("/ninput   callnum=");  
  scanf("%d",&callnum);  
  del(location,callnum);  
  }  
  三、以类作为结点来处理约瑟夫问题(准C++编程风格)  
  1、以类作结点的链表建立  
  #include    
  class   Node  
  {  
  private:  
  int   data;  
  Node   *next;  
  public:  
  Node(){data=0;next=NULL;}  
  void   SetData(int   new_data){data=new_data;}  
  void   SetNext(Node   *new_next){next=new_next;}  
  int   GetData(){return   data;}  
  Node   *GetNext(){return   next;}  
  };  
  void   main()  
  {  
  Node   *head=NULL,*p,*q;  
  for(int   i=1;i<9;i++)  
  {  
  p=new   Node;  
  p->SetData(i);  
  if(head==NULL)  
  head=p;  
  else  
  q->SetNext(p);  
  q=p;  
  }  
  q=head;  
  do  
  {  
  cout<<"该游客编号为:"<GetData()<  q=q->GetNext();  
  }while(q!=NULL);  
  q=head;  
  do  
  {  
  q=q->GetNext();  
  delete   head;  
  head=q;  
  }while(q!=NULL);  
  }  
  2、以类作结点的循环链表的建立  
  #include    
  class   Node  
  {  
  private:  
  int   data;  
  Node   *next;  
  public:  
  Node(){data=0;next=NULL;}  
  void   SetData(int   new_data){data=new_data;}  
  void   SetNext(Node   *new_next){next=new_next;}  
  int   GetData(){return   data;}  
  Node   *GetNext(){return   next;}  
  };  
  void   main()  
  {  
  Node   *head,*p,*q;  
  head=new   Node;  
  q=p=head;  
  for(int   i=1;i<=8;i++)  
  {    
  p->SetData(i);  
  p->SetNext(head);  
  q->SetNext(p);  
  q=p;  
  p=new   Node;  
  }  
  q=head;  
  i=1;  
  do  
  {  
  cout<<"该游客编号为:"<GetData()<  q=q->GetNext();  
  i++;  
  }while(i<=10);  
  }  
  3、解决约瑟夫问题  
  #include    
  class   Node  
  {  
  private:  
  int   data;  
  Node   *next;  
  public:  
  Node(){data=0;next=NULL;}  
  void   SetData(int   new_data){data=new_data;}  
  void   SetNext(Node   *new_next){next=new_next;}  
  int   GetData(){return   data;}  
  Node   *GetNext(){return   next;}  
  };  
  void   main()  
  {  
  Node   *head,*p,*q;  
  head=new   Node;  
  q=p=head;  
  for(int   i=1;i<=8;i++)  
  {  
  p->SetData(i);  
  p->SetNext(head);  
  q->SetNext(p);  
  q=p;  
  p=new   Node;  
  }//  
  p=head;  
  i=1;  
  while(i<=8)  
  {  
  cout<GetData()<<"   "<  p=p->GetNext();  
  i++;  
  }//输出  
  cout<  p=head;  
  while(p->GetNext()!=p)  
  {  
  i=1;  
  while(i<2)  
  {  
  p=p->GetNext();//将欲删除点的前一个结点  
  i++;  
  }  
  q=p->GetNext();  
  cout<GetData()<  p->SetNext(q->GetNext());//将q指针域所指结点的地址赋给p的指针域  
  p=p->GetNext();  
  delete   q;  
  }//做循环数数出局游戏  
  cout<<"/nLast   One   "<GetData()<  }  
  四、用标准的面向对象编程风格处理约瑟夫问题(C++编程风格)  
  //#include   "stdafx.h"  
  #include   "iostream.h"  
  //#define   NULL   0  
  class   Node  
  {  
  private:  
  int   data;  
  Node   *next;  
  public:  
  Node(){data=0;next=NULL;}  
  Node   *Create(int   n);//创建含n个结点的循环链表  
  void   Output(Node   *p,int   n);//输出循环链表头结点为p的后n个结点的信息  
  Node   *Move(Node   *p,int   n);//将头结点指针前移到n  
  //从头结点为p的循环链开始,所用的计数为n进行约瑟夫实验  
  void   Josephus(Node   *p,int   n);  
  };  
  Node   *Node::Create(int   n)  
  {  
  Node   *head,*p,*q;  
  head=new   Node;  
  q=p=head;  
  for(int   i=1;i<=n;i++)  
  {  
  p->data=i;  
  p->next=head;  
  q->next=p;  
  q=p;  
  p=new   Node;  
  }  
  return   head;  
  };  
  void   Node::Output(Node   *p,int   n)  
  {  
  int   i=1;  
  while(i<=n)  
  {  
  cout<data<<"   ";  
  p=p->next;  
  i++;  
  }  
  };  
  Node   *Node::Move(Node   *p,int   n)  
  {  
  if(n>1)  
  {  
  int   i=1;  
  while(i  {  
  p=p->next;  
  i++;  
  }  
  }  
  return   p;  
  };  
  void   Node::Josephus(Node   *p,int   n)  
  {  
  Node   *q;  
  while(p->next!=p)  
  {  
  p=Move(p,n-1);  
  q=p->next;  
  cout<data<<"   ";  
  p->next=q->next;  
  p=p->next;  
  delete   q;  
  }  
  cout<<"/nLast   One   "<data<  };  
  void   main()  
  {   Node   A,*head;  
  head=A.Create(8);  
  cout<<"/nCirclist   is   ";  
  A.Output(head,10);  
  head=A.Move(head,1);  
  cout<<"/nJosephus   result   is   "<  A.Josephus(head,3);  
  }  
  五、对两种编程风格的评述  
  在进行面向过程的程序设计时,一般首先考虑程序所要实现的功能,然后设计为实现这些功能所必须采取的步骤,这些步骤就是过程。如果一个过程比较复杂而不能直接使用已有的抽象进行实现,则对这个过程进行分解,使分解之后的每一步(更低级的过程)能够直接对应着一条语句。通过将分解之后的一系列过程封装在一个函数抽象中,程序员在特定的时刻只关心有限的细节,这个新的函数抽象比其较低级的抽象更接近问题求解的过程,因而,能够很好地映射问题求解中的过程。如果这个过程出现在许多问题求解中,那么,这个函数抽象就可能被重复利用。  
  函数是面向过程程序设计的基础,按照结构化程序设计的思想,又可将完成某一复杂工作的函数放在一个头文件,便于我们多次复用。  
  面向过程的程序设计方法与面向对象的程序设计方法的根本区别在于对待数据和函数的关系上。  
  在面向过程的程序设计中,数据只被看作是一种静态的结构,它只有等待调用函数来对它进行处理。  
  在面向对象的程序设计中,将数据和对该数据进行合法操作的函数封装在一起作为一个类的定义。另外,封装还提供了一种对数据访问严格控制的机制。因此,数据将被隐藏在封装体中,该封装体通过操作接口与外界交换信息。  
  面向对象的思想需要在实践中不断摸索和体会,在以后的程序设计中,可主动运用这种思想去实践。

你可能感兴趣的:(数据结构,算法,ACM,output,null,struct,编程,delete,class)