单向循环链表实现约瑟夫环(java版)

上一篇 / 下一篇  2008-05-07 16:20:14 / 个人分类:数据结构

1.背景资料
&Z3EbM%CZ&X/Zq:Q0

是一个数学的应用问题:ITPUB个人空间;M:T?L"~
ITPUB个人空间N9](|5Y9E8u
    已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。ITPUB个人空间}9P/pK1`/SA7~0v

kKX(ZK i d^0
链表方法

'rvo9Y/dn0   这个就是约瑟夫环问题的实际场景,有一种是要通过输入n,m,k三个正整数,来求出列的序列。这个问题采用的是典型的循环链表的数据结构,就是将一个链表的尾元素指针指向队首元素。 p->link=headITPUB个人空间4KBh ?s/\[9@

GO-rbZ7a+Q ZP0   解决问题的核心步骤:
(YcW iN8?n j E0       1.建立一个具有n个链结点,无头结点的循环链表
[t guN|-J Ic0       2.确定第1个报数人的位置
C;j)zV8II0       3.不断地从链表中删除链结点,直到链表为空
ZYHt+M[0
5M[ EdR3{!w2y6N0void JOSEPHUS(int n,int k,int m) //n为总人数,k为第一个开始报数的人,m为出列者喊到的数
1KP;o#Za#R0{
7I:^ n d%T;Q7[ SNC0    /* p为当前结点  r为辅助结点,指向p的前驱结点  list为头节点*/
hED"lM h0    LinkList p,r,list;
D~#b0DD|0ITPUB个人空间&~ gV,s4Sc$g
    /*建立循环链表*/
cK-Q8K9s&z W0    for(int i=0,i<n,i++)ITPUB个人空间"sHL-v$RU8C0I7~u
    {
$PbYt Y;^^*r ^aS0        p=(LinkList)malloc(sizeof(LNode));
'gV(yh kl?|0        p->data=i;ITPUB个人空间\-I_,F Y$l0F g
        if(list==NULL)ITPUB个人空间#O `K}8H2kqE
            list=p;ITPUB个人空间6r`5c)Rv D$Fd6A~/C
        elseITPUB个人空间7M*x3vo#r,r9{-B'c u
            r->link=p;ITPUB个人空间v#UWu2wR R
        r=p;
0]2Y"C:O&x `0    }
|.`G"]lUfqa0    p>link=list; /*使链表循环起来*/ITPUB个人空间5e9p af*@;J
    p=list; /*使p指向头节点*/ITPUB个人空间9|)U"W],a!j/L{

V4SmZ6vA0    /*把当前指针移动到第一个报数的人*/ITPUB个人空间)r5O^I*abZ0n$I^#JIS
    for(i=0;i<k;i++)ITPUB个人空间A iR3`a7Yk&{
    {ITPUB个人空间6Vlt}G
        r=p;ITPUB个人空间+X$\$M6CY
        p=p->link;
t7Sg!c'Pc_M.C0    }ITPUB个人空间sT6i|5`N
ITPUB个人空间 DR#t HH6Z2v-e)[-I*n
    /*循环地删除队列结点*/
0a[nbS*p&q6s"A{0    while(p->link!=p)ITPUB个人空间SD"C%B6~ y(h0g"qy7g5s
    {ITPUB个人空间3q&Yj Z0Mui^
        for(i=0;i<m;i++)ITPUB个人空间O ah2ua
        {ITPUB个人空间6d\;i|J a4{ h5Y6C
            r=p;ITPUB个人空间 ^~npE%v6|p kWq}
            p=p->link;
v/t0}L0Z&Qaxp0        }ITPUB个人空间2{6K*H?}p%nD:r4_6Y
        r->link=p->link;ITPUB个人空间7?s7\sl/[)i
        printf("被删除的元素:%4d ",p->data);ITPUB个人空间|YWs X it
        free(p);ITPUB个人空间3k3C-L(JY
        p=r->link;
IMtm\6Ps0    }ITPUB个人空间L~;e5Eu`U)X0cX
    printf("\n最后被删除的元素是:%4d",P->data);
D^g3Nx-K0}ITPUB个人空间7Zb'Mk:I
ITPUB个人空间$DG\$Fcy+zUA
ITPUB个人空间,v| B+c5Q
ITPUB个人空间C,mTep-y9G |1`'G
Josephus(约瑟夫)问题的数学方法
ITPUB个人空间#\ j'\n3I^:T;XK M
ITPUB个人空间2z o_Q2D ?;X
     无论是用链表实现还是用数组实现都有一个共同点:要模拟整个ITPUB个人空间e-tAH/m{!l3IX
游戏过程,不仅程序写起来比较烦,而且时间复杂度高达O(nm),当nITPUB个人空间K? vMG
,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间ITPUB个人空间DnhT3vL_B
内出结果的。我们注意到原问题仅仅是要求出最后的胜利者的序号,
L!i dBbO3|IYl9B;[0而不是要读者模拟整个过程。因此如果要追求效率,就要打破常规,
|;w@[#K/f"H0h0实施一点数学策略。
p qvg-{ ^"s8N u+U0为了讨论方便,先把问题稍微改变一下,并不影响原意:ITPUB个人空间Z ~0q [&zp
问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出
.G E0j6Hc-Z;Fl0,剩下的人继续从0开始报数。求胜利者的编号。ITPUB个人空间$ui#qe([)m:P@
我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组ITPUB个人空间0f/S"F,H U-n
成了一个新的约瑟夫环(以编号为k=m%n的人开始):ITPUB个人空间"RgXx/U0c c+b.J'NX
   k   k+1   k+2   ... n-2, n-1, 0, 1, 2, ... k-2ITPUB个人空间'LB,RzC%I.|/f P
并且从k开始报0。ITPUB个人空间9kBf2L VQ5yW;I0r
现在我们把他们的编号做一下转换:ITPUB个人空间gNY(T'm|z,C2Q^Jg~
k      --> 0
/KXS.o L0k+1    --> 1ITPUB个人空间n dk\ m#`q
k+2    --> 2ITPUB个人空间 V7xp^,J |F%Pz
...
V6Wc G2p3?:L0...
.Jlotu'EgH"M0k-2    --> n-2ITPUB个人空间Eb$Hi cJ:V
k-1    --> n-1ITPUB个人空间Z0M[n ZM7z D
变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这ITPUB个人空间'S1A2gU.O)|+tb
个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x
1v1v#j_L0变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相ITPUB个人空间E] ~lV#Z9[
信大家都可以推出来:x‘=(x+k)%nITPUB个人空间+kZ6D Jz nIA
如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就
(il6f;^3~-h0行了。(n-2)个人的解呢?当然是先求(n-3)的情况 ---- 这显然就是ITPUB个人空间4}&kN4^%vC
一个倒推问题!好了,思路出来了,下面写递推公式:
UT7MB-i#t y+D4b0ITPUB个人空间u ]%Q!`$aWWZ)M
令f表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然
b%fYM1|.er L-q0是f[n]
N$U@g#d;f&`]0递推公式
WSo$EL,C;[0f[1]=0;ITPUB个人空间&]h5|u9]j3BZ
f=(f[i-1]+m)%i;   (i>1)
#c%W,aa{8P!GR0有了这个公式,我们要做的就是从1-n顺序算出f的数值,最后结ITPUB个人空间f&r.H*oJ!t r$D
果是f[n]。因为实际生活中编号总是从1开始,我们输出f[n]+1
fe}w n D`M%N0由于是逐级递推,不需要保存每个f,程序也是异常简单:
3["U d#C%i"nN6N0#include <stdio.h>ITPUB个人空间$Bq~o!bF
intmain(void)
3R|7zU1m)N y0{ITPUB个人空间0C@B1[CB;Xgq\
   intn, m, i, s=0;
)k(ds\ {'k5^0   printf ("N M = "); scanf("%d%d", &n, &m);ITPUB个人空间 OMu6LLb
   for (i=2; i<=n; i++) s=(s+m)%i;ITPUB个人空间/q0]L@ I K1L3?
   printf ("The winner is %d\n", s+1);ITPUB个人空间;HB!I,X+XVN T x
}
vw$h(H*K fYiEN0这个算法的时间复杂度为O(n),相对于模拟算法已经有了很大的提高ITPUB个人空间B1oTK0v-d3jp6M
。算n,m等于一百万,一千万的情况不是问题了。可见,适当地运用
%wLKy UX.N#U^0数学策略,不仅可以让编程变得简单,而且往往会成倍地提高算法执ITPUB个人空间v1@S#~X#o7|-T
行效率。
l Vy&Zq:X K!s A0

2.实现代码

(1).LinkNode  

package zhangy;

 

public class LinkNode {

      

public Object data;

private LinkNode next;

LinkNode(Object data)

{

      this.data=data;

      }

LinkNode()

{

 

      }

public LinkNode getNext() {

      return next;

}

public void setNext(LinkNode next) {

      this.next = next;

}

public Object getData() {

      return data;

}

public void setData(Object data) {

      this.data = data;

}

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(2). ROneLinkList

package zhangy;

 

public class ROneLinkList implements List{

      protected Object data;

      protected LinkNode head;

      int size;

   //构造空链表

      ROneLinkList()

      {

       head=new LinkNode();

       head.setNext(head);

 

       size=0;

             

      }

      

      public void insertElement(int i,Object o)throws Exception

      {

             //如果在0位置或者大于size的位置

             if(i<=0||i>size+1)

             {

                    throw new Exception("位置错误");

             }

             LinkNode newnode=new LinkNode(o);

             LinkNode temp=head;

             //如果现在0个元素

             if(head==head.getNext())

             {

                    head.setNext(newnode);

                    newnode.setNext(head);

                    

                    

             }

      

             

             //普通情况

             else

             {

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

             {

                    temp=temp.getNext();

             }

             newnode.setNext(temp.getNext());

             temp.setNext(newnode);

             }

             

             

             

             size++;

             

             

             

      }

      

      public void deleteElement(int i) throws Exception

      {

             if(i<=0||i>size)

             {

                    throw new Exception("位置出错");

             }

             

             

             

             LinkNode temp=head;

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

             {

                    temp=temp.getNext();

             }

             temp.setNext(temp.getNext().getNext());

             size--;

             }

      

 

 

      

 

      public Object getElement(int i) throws Exception

      {

             if(i<1||i>size)

             {

                    throw new Exception("位置错误");

             }

             LinkNode temp=head;

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

             {

                    temp=temp.getNext();

             }

             return temp;

             

             

      }

      public int getSize()

      {

             return size;

      }

      

      public boolean isEmpty()

      {

             if(size==0)return true;

             else return false;

      }

 

      

 

}

 

(3). Josephus

package zhangy;

 

public class Josephus extends ROneLinkList{

      

      Josephus(int n)

      {

             

             super();

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

             {

                    try{

                    insertElement(i,Integer.valueOf(i));

                    }catch(Exception e)

                    {

                           e.getMessage();

                    }

                    

                    

                    

                    

             }

      }

             public void display(int s,int d)

             {

             

                    LinkNode temp=head;

                    for(int i=1;i<s;)

                    {

                           

                           temp=temp.getNext();

                           if(temp!=head)

                           {

                                  i++;

                           }

                           

                           

                    }

                    

                    

                    do{

                           

                           

                    for(int i=1;i<d;)

                    {

                           

                           temp=temp.getNext();

                           if(temp!=head)

                           {

                                  i++;

                           }

                           

                           

                    }

                    

          if(temp.getNext()!=head)

          {

                    temp.setNext(temp.getNext().getNext());

          }else

          {

         temp.getNext().setNext(temp.getNext().getNext().getNext());

          }

         

                    this.output();

      

             

                    

                    }while(head!=head.getNext());

                    

             }

             

             

TAG:

引用 删除 Guest   /   2008-10-07 10:11:43
5
引用 删除 Guest   /   2008-09-14 14:01:05
5
引用 删除 Guest   /   2008-08-15 15:20:50
5
 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

日历

« 2008-10-11  
   1234
567891011
12131415161718
19202122232425
262728293031 

数据统计

  • 访问量: 493
  • 日志数: 1007
  • 建立时间: 2008-04-29
  • 更新时间: 2008-05-13

RSS订阅