一、实验目的

    使用队列数据结构,模拟Josephus排队问题,根据输入的总人数和间隔数,求出最后一位幸存者,并在屏幕上可视化输出出来。

  Josephus排队问题(C++描述)

 

二、实验思路

    采用链队列模拟排队,根据总人数初始化并创建链队。当在队里的总人数大于1个人时,遍历链队里所有节点。每访问一个就让它出队,如果它满足继续排队的条件,再让它进队插在队尾(即这个节点的序号不是循环间隔的倍数),反映到实际问题上就是这个人还可以存活。如此反复进行下去,直到链队里还剩下最后一个节点。

    频繁的出队和入队是这种算法的缺点,但是由于队列“先进先出”的性质,无法在队列里查找待出队的元素,然后使其前驱指针指向其后继。所以如果这个实验不用队列,而用其他数据结构,比如操作不受限的链表,可能效率会更高。

    因为频繁地出队和入队,所以不适合用顺序结构实现此实验。

 

三、链队定义

    根据业务要求,链队中每个节点需要包括的成员为:int num,double x,double y,void *object,QNode *next. 其中,num是每个节点的序号,x,y是每个节点在屏幕上的位置,object是下文为实现动态绘制与消除所定义的一个void型指针变量,next是指向下一个节点的QNode型指针变量。

    Josephus排队问题(C++描述)

每个节点依靠next指针变量连接成一个链表,对这个链表再作些限制便构成了链队。链队的成员为:     int nSize,int interval,QNode *front,QNode *rear.

其中,nSizeJosephus问题的总人数,interval是间隔人数,这里将这两个从键盘输入的参数定义为链队的成员。front,rear分别是链队的头指针和尾指针,它们都是QNode型指针变量,且节点从front出队,从rear入队。

    Josephus排队问题(C++描述)

 

四、链队行为

    int InitQueue(LinkQueue *Q,int nSize,int interval);

    Josephus排队问题(C++描述)

    main函数里传入链队“宏观意义上的”指针以及相关参数,在Init()里对链队的相关属性进行设置。

 

    int DestroyQueue(LinkQueue *Q);

    Josephus排队问题(C++描述)

    程序运行结束时要销毁这个链队,也就是要销毁链队里的每个节点,所以要对NULL值进行判断。如果非空,则遍历每个节点并销毁。最后销毁头结点,并赋值为NULL

 

    int CreateQueue(LinkQueue *Q,double cx,double cy,double radius);

    Josephus排队问题(C++描述)

    因为最后想实现的效果是让每个节点排成一个圆,所以在这里传入确定圆位置与形状的圆心坐标cx,cy和半径radius。在屏幕上画出圆上的每个点,是通过定出其在屏幕上的x,y坐标实现的。在这个函数里,每生成并入队一个节点,就计算出它的位置。确定圆顶点位置的算法为:
    Josephus排队问题(C++描述)

    nSize是圆的顶点数,即链队中的节点数,也就是实际问题中的总人数。这里的nSize是作为链队的一个属性实现的,并非参数传递。

    调用自定义DSGraphic库里的函数drawPoint(),drawText()绘制圆上的顶点和对应的序号。

 

    int EnQueue(LinkQueue *Q,double x,double y,int num);

    Josephus排队问题(C++描述)

    链队的入队操作,这里新生成的入队节点的相关属性(x,y,num)是从函数外部传来的,要结合下面的函数看。

 

    int DeQueue(LinkQueue *Q,double *x,double *y,int *num);

    Josephus排队问题(C++描述)

    链队的出队操作,因为Josephus问题的业务需要,对出队的节点,保存其相关信息(x,y,num),以便以后入队时为新节点赋值。

    这里要注意,链队的出队操作,要防止误删尾指针。指的是如果要出队的是最后一个节点,那么要保存尾指针,使尾指针指向头结点。因为尾指针总是指向最后一个节点,如果直接就这样把最后一个节点删除了,尾指针也就丢失了。

 

    void DeadCircleLogic(LinkQueue *Q);

    Josephus排队问题(C++描述)

这个函数是整个算法的核心,count用于记录到找到最后一位幸存者一共进行的判断次数。num,x,y记录出队节点的信息,以便下次再显示时可以显示出这些信息。nSize是链队中的节点数,当入队时nSize1,出队减1。当nSzie不等于1时,就一直执行循环体内的语句。

    总的思想就是,对遍历到的每个节点都出队,如果它不是interval的倍数再让它入队。如此反复进行,直到找到最后一个“幸存者”。

    这里clearObject(),Q->rear->object=drawPoint(…)的作用都是为了更好的显示效果,与自定义的DSGraphic库函数有关。

 

五、main函数

    Josephus排队问题(C++描述)

    输入Josephus问题要求的人数nSize和间隔数interval,并对输入的正确性进行检查。setPlayingSpeed()是自定义的与绘制图形的速度有关的函数,与DSGraphic函数库有关。

    定义链队变量Q并指向一个新节点。初始化这个链队,传入链队的“宏观上的”指针,为链队设置各项参数。根据输入的(400,200,150)(x,y,radius)信息,入队每一个节点,并计算每一个节点在屏幕上的坐标,最后把它们画出来。执行核心算法逻辑,对Josephus问题进行求解,最后销毁这个链队。

 

六、运行结果
    Josephus排队问题(C++描述)
Josephus排队问题(C++描述)
Josephus排队问题(C++描述)
Josephus排队问题(C++描述)
Josephus排队问题(C++描述)


    



相关文章: