约瑟夫问题及扩展问题的代码实现

下面两个博客的内容给了我很大的帮助,基本上都是参考他们的内容。

http://blog.csdn.net/u013850478/article/details/23037423 

http://blog.sina.com.cn/s/blog_3cb8b94501017lpr.html

首先约瑟夫的基本问题里,可以推导出递推公式,有一个递推方法比较详细,忘了在哪个博客上看到的,我只是复述一遍,过程如下图:

有了递推公式,解决基本问题(最后一个人的编号,过程不需要输出)就是轻而易举的事。

下面代码包括几个方法,分别是:

解决基本问题,O(n)时间复杂度,编号从0开始;

解决基本问题,O(n)时间复杂度,编号从1开始;

解决基本问题,O(NM)时间复杂度,编号从0开始,采用环模拟报数的过程,不过这种方法可以输出依次出队的编号(我有个疑问是,如果要依次输出出队编号的话,还有什么更有效的方法);

扩展问题1,当首次报到m出队和之后报到k出队的m和k不同时;

扩展问题2,第P个出队的编号是多少;

扩展问题3,结合扩展问题1和扩展问题2的情况;

扩展问题4,当M=2时,这种特殊情况下,复杂度可降到O(logN),不过我的代码用了递归,没有直接用迭代,不知道用迭代怎么把这个问题写出来;

  1 package Source;
  2 class circleNode{
  3     int data;
  4     circleNode next;
  5     public circleNode(int _data, circleNode n){
  6         data = _data;
  7         next = n;
  8     }
  9 }
 10 /**
 11  * 从0开始编号的地推公式:f(n,m) = [f(n-1,m)+m]%n ; f(1,m) = 0;
 12  * 从1开始编号的递推公式:f(n,m) = [f(n-1,m)+m -1]%n +1; f(1,m) = 1;
 13  * @author Echo
 14  *
 15  */
 16 public class CircleNM {
 17     /**
 18      * 约瑟夫问题的基本问题:n个人围成一圈,从0开始编号到n-1,从1报数到m,数到m的出队,然后从下一个开始数1,报到m的输出队列,
 19      * 求最后一个人的编号。
 20      * 这里采用数学方式f(n,m) = [f(n-1,m)+m]%n  O(n)
 21      * 如果编号是从1开始,则最后输出的时候加1
 22      * 采用递推的方法解决
 23      * @param n
 24      * @param m
 25      * @return
 26      */
 27     public static int firstMathMethod(int n,int m)
 28     {
 29         if(n<=0 || m<=0)
 30             return -1;//输入参数不对
 31         int lastVal = 0;
 32         for(int i=2;i<=n;i++)
 33             lastVal = (lastVal+m)%i;
 34         return lastVal;
 35     }
 36     public static int firstMathMethod2(int n,int m)
 37     {
 38         if(n<=0 || m<=0)
 39             return -1;//输入参数不对
 40         int lastVal = 1;
 41         for(int i=2;i<=n;i++)
 42             lastVal = (lastVal+m-1)%i +1;
 43         return lastVal;
 44     }
 45     /**
 46      * 问题同上,但是用链表
 47      * 这种方法可以输出依次出队的编号,目前我不知道除了模拟方法,还有什么更有效率的方法可以依次输出这个顺序??
 48      * 采用环模拟删除过程 O(nm)
 49      * @param n
 50      * @param m
 51      * @return
 52      */
 53     public static int secondLinkedListMethod(int n, int m)
 54     {
 55         //创建环形
 56         circleNode head = new circleNode(0,null);
 57         circleNode pointer =head;
 58         for(int i=1;i<n;i++)
 59         {
 60             pointer.next = new circleNode(i,null); 
 61             pointer = pointer.next;
 62         }
 63         pointer.next = head;
 64         //模拟删除过程
 65         pointer = head;
 66         int index = 1;
 67         while(pointer.next!=pointer)
 68         {
 69             if(index == m-1)
 70             {
 71                 pointer.next = pointer.next.next;
 72                 pointer = pointer.next;
 73                 index = 1;
 74             }else
 75             {
 76                 index++;
 77                 pointer =  pointer.next;
 78             }
 79         }
 80         return pointer.data;
 81         
 82     }
 83     
 84     /**
 85      * 这算是约瑟夫问题的一个扩展:n 个人按顺时针围成一圈从1开始按顺序顺序编号,首先先把第m号的人出队,
 86      * 然后从m+1号开始按1、2、3 、....、k按顺时针报数,报 k 者退出圈外,
 87      * 其余的人再从1、2、3 、....、k 报数,报 k 的人再退出圈外,依次类推。请输出最后一个人的原序号。
 88      * 其实整个思维过程和基本问题是一制的。
 89      * @param n 如果编号从1开始,则 最后输出的数+1 即可
 90      * @param k
 91      * @param m
 92      * @return
 93      */
 94     public static int genericNMK(int n, int k, int m)
 95     {
 96         if(n<=0 || m<=0)
 97             return -1;//输入参数不对
 98         int lastValue = 0;
 99         for(int i=2;i<n;i++)
100             lastValue = (lastValue + k)%i;
101         return (lastValue +m)%n;
102     }
103     142     /**
143      * 扩展问题2:第P轮出圈的编号,这个r初始的时候为什么是0,我不懂???
144      * 这里是从1开始编号
145      * @param n
146      * @param m
147      * @param p
148      * @return
149      */
150     public static int NMP(int n,int m, int p)
151     {
152         int r = 0;
153         for(int i=n-p+1;i<=n;i++)
154         {
155             r = (r+m-1)%i +1;
156         }
157         return r;
158     }
159     /**
160      * 扩展问题3,首次出队的报道数和之后的不同
161      * @param n 从1开始编号到n
162      * @param m 除了首次外都按照
163      * @param k 首次出队的编号是报道k的
164      * @param p 第p个出圈
165      * @return
166      */
167     public static int NMKP(int n,int m,int k, int p)
168     {
169         int r = 0;
170         for(int i=n-p+1;i<n;i++)
171         {
172             r = (r+m-1)%i +1;
173         }
174         return (r+k-1)%n+1;
175     }
176     /**
177      * 一种特殊情况下,即m=2时,可以得到O(logN)的算法
178      * 可以再想下直接迭代怎么做??
179      * 采用递归的方式解决上面的特殊问题
180      * @param n
181      * @return
182      */
183     public static int N2recu(int n)
184     {
185         if(n<=0)
186             return -1;
187         if(n == 1)
188             return 1;
189         if(n%2 == 0)
190             return 2*N2recu(n/2)-1;
191         else
192             return 2*N2recu(n/2)+1;
193     }
194     /**
195      * @param args
196      */
197     public static void main(String[] args) {
198         // TODO Auto-generated method stub
199         //System.out.println(firstMathMethod(10,11));
200         //System.out.println(firstMathMethod2(10,11));
201         //System.out.println(secondLinkedListMethod(8,3));
202         
203         System.out.println(N2recu(11));
204     }
205 
206 }
有问题可以和我联系,bettyting2010#163 dot com
原文地址:https://www.cnblogs.com/echoht/p/3674795.html