利用循环链表实现约瑟夫问题的求解

 

【实验目的】

1.  熟悉C++语言的基本编程方法,掌握集成编译环境的调试方法;

2.  学习指针、模板类、异常处理的使用;

3.  掌握线性表的操作实现方法;

4.  培养使用线性表解决实际问题的能力。

【实验内容】

      利用循环链表实现约瑟夫问题的求解。

      约瑟夫问题如下:已知n个人(n>=1)围坐一圆桌周围,从1开始顺序编号。从序号为1的人开始报数,顺时针数到m的那个人出列。他的下一个人又从1开始报数,数到m的那个人又出列。依此规则重复下去,直到所有人全部出列。请问最后一个出列的人的编号。

2.程序分析

2.1 存储结构

     存储结构:循环链表

1

 

2

 

3

 

n

 

……

first

2.2 关键算法分析

【设计思想】

首先,设计实现约瑟夫环问题的存储结构。由于约瑟夫环本身具有循环性质,考虑采用循环链表,为了统一对表中任意节点的操作,循环链表不带头结点。循环链表的结点定义为如下结构类型:

struct Node

 {

 int number;

 Node *next;

};

其次,建立一个不带头结点的循环链表并由头指针first指示。

最后,设计约瑟夫环问题的算法。

【伪代码】

1、  工作指针first,r,s,p,q初始化

2、  输入人数(n)和报数(m)

3、  循环n次,用尾插法创建链表

         Node *q;

          for(int i=1;i<=n;i++)

          {

           Node *p;

           p=new Node;

           p->number=i;

           p->next=NULL;

          if(i==1) L=q=p;

          else             

          {

           q->next=p;

           q=q->next;

          }

         }

         q->next=L;

         if(L!=NULL){return(L);}

      4、输入报数的起始人号数k;

      5、Node *q = new Node;计数器初始化i=1;

6、循环n次删除结点并报出位置(其中第一个人后移k个)

   当i<n时

       移动指针m-2次p=p->next;

       删除p结点的后一结点q

q=p->next;

                   p->next=q->next;

                   *L = p->next;

             报出位置后Delete q;

       计数器i++;

【复杂度】

for(int i=1;i<=n;i++)

 {

  Node *p;

  p=new Node;

  p->number=i;

  p->next=NULL;

  if(i==1) L=q=p;

  else             

  {

   q->next=p;

   q=q->next;

  }

时间复杂度:O(n)

if(i==1) i+=LengthList(*L);

 Node *p;

 p=*L;

 int j=0;

 while(j<i-2) {p=p->next;j++;}

 q = p->next;

 p->next=p->next->next;

 *L = p->next;

 return(q);

时间复杂度:O(n2

算法的空间复杂度:O(n2

 

2.3 其他

程序源代码:

#include<iostream>

using namespace std;

struct Node//循环节点的定义

{

 int number;//编号

 Node *next;

};

Node *CreateList(Node *L,int &n,int &m);//建立约瑟夫环函数

void Joseph(Node *L,int n,int m);//输出每次出列号数函数

Node *DeleteList(Node **L,int i,Node *q);//寻找每次出列人的号数

int LengthList(Node *L);//计算环上所有人数函数

void main()//主函数

 Node *L;

 L=NULL;//初始化尾指针

 int n, m;

 cout<<"请输入人数N:";

 cin>>n;//环的长度

 if(n<1){cout<<"请输入正整数!";}//人数异常处理

 else

 {

  cout<<"请输入所报数M:";

  cin>>m;

  if(m<1){cout<<"请输入正整数!";}//号数异常处理

  else

  {

  L=CreateList(L,n,m);//重新给尾指针赋值

  Joseph(L,n,m);

  }

 }

system("pause");

}

Node *CreateList(Node *L,int &n,int &m)//建立一个约瑟夫环(尾插法)

{

 Node *q;

 for(int i=1;i<=n;i++)

 {

  Node *p;

  p=new Node;

  p->number=i;

  p->next=NULL;

  if(i==1) L=q=p;//工作指针的初始化

  else             

  {

   q->next=p;

   q=q->next;

  }

 }

 q->next=L;

 if(L!=NULL){return(L);}//返回尾指针

 else cout<<"尾指针异常!"<<endl;//尾指针异常处理

}

 

void Joseph(Node *L,int n,int m)//输出每次出列的人

{

 int k;

 cout<<"请输入第一个报数人:";

 cin>>k;

 if(k<1||k>n){cout<<"请输入1-"<<n<<"之间的数"<<endl;}

 else

 {

  cout<<"\n出列顺序:\n";

 

  for(int i=1;i<n;i++)

  {

   Node *q = new Node;

   if(i==1) q=DeleteList(&L,k+m-1,q);//第一个出列人的号数

   else q=DeleteList(&L,m,q);

   cout<<"号数:"<<q->number<<endl;

   delete q;//释放出列人的存储空间

  }

  cout<<"最后一个出列号数是:"<<L->number<<endl;;//输出最后出列人的号数

 }

}

 

Node *DeleteList(Node **L,int i,Node *q) //寻找每次出列的人

{

 if(i==1) i+=LengthList(*L);//顺序依次出列情况的处理方式

 Node *p;

 p=*L;

 int j=0;

 while(j<i-2) {p=p->next;j++;}

 q = p->next;

 p->next=p->next->next;

 *L = p->next;

 return(q);

}

 

int LengthList(Node *L)//计算环上的人数

{

 if(L){cout<<"尾指针错误!"<<endl;}//异常处理

 else

 {

  int i=1;

  Node *p=L->next;

  while(p!=L)

  {

   i++;

   p=p->next;

  }

  return(i);

 }

}

3.程序运行结果

1.测试主函数流程:

开始

输入m和n

创建链表

k>n-1

移动指针p

删除p后一结点q

指针p后移,k++

输出n

结束

Y

N

2.测试条件:如上图所示,人数为20人,所报数为6,第一个报数的人是1号。

3.测试结论:得出最后出列的人是20号,与算得的结果一致,证明程序正常运行,能够解决一般的约瑟夫环问题。

4.总结   

1.调试时出现的问题及解决办法:

利用带头结点的尾插法建立链表求解的时候,头节点的作用无法确定,导致编译通过,但是运行之后的结果都不是正确的运行结果。苦苦思索,包括和同学讨论,一直没能解决,最后决定改用另一种存储方法,将头节点直接改成NULL,最终测试的结果是正确的。(但是还未能完全理解原因是什么)

用函数返回存储节点的地址的时候,函数中的一句没有问题的语句出现访问错误,更改函数从而解决了问题。

2.心得体会:

这次做数据结构作业,不仅对开学一段时间内所学的知识有了更好的理解,而且很好地锻炼了自己的编程能力。发现心中了解程序的主要算法和真正写出来完全不是一回事,一开始做多项式的时候就是先写函数,后定义存储结构等,结果编译报错很多,不知道怎么修改,无奈只好改成做约瑟夫环问题了。在编程和写报告的过程中曾多次遇到各种各样的问题,通过与同学的交流以及自己独立思考,最终得到解决并顺利的完成了此次作业。总之一句话,获益良多。

3.下一步的改进:

下次作业如果时间允许的话,我要选择最难的,而且要全程独立去编,实在解决不了的问题才去请教老师或者同学
  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值