Josephus环问题


约瑟夫环问题


  • 问题描述:

Josephus问题可以描述为如下的一个游戏:N个人编号从1到N,围坐成一个圆圈,从1号开始传递一个热土豆,经过M次传递后拿着土豆的人离开圈子,由坐在离开的人的后面的人拿起热土豆继续进行游戏,直到圈子只剩下最后一个人。例如:M=0,N=5,则游戏人依次被清除,5号最后留下;如果M=1,N=5,那么被清除的人的顺序是2,4,1,5,最后剩下的是3号。

  • 如下是两种解题方法:

    • 建立一个N大小的数组,存储N个人是否还在圈子内(0为在圈子内,-1为已经离开圈子),依次循环遍历整个数组,直到剩下的人数(left)为1。
    •   public static void pass(int m, int n){
              int[] num = new int[n];
              int left = n;  //剩下的人数
              int index = 0; //当前遍历到的位置
              while(left != 1){
                  for(int i = 0; i< m; i++){
                      if(num[index++] != 0)   //如果当前人已经清除
                          i--;
                      if(index >= n)
                          index = 0;
                  }
                  while(num[index] != 0){
                      index++;
                      if(index >= n)
                      index = 0;
                  }
                  System.out.println("out number is " + (index + 1));
                  num[index] = -1;    //将清除的数据下标置-1
                  index++;
                  if(index >= n)
                      index = 0;
                  left--;
              }
          }
    • 第二种方式,将1~N的数添加到一个ArrayList对列中,首先计算偏移量(mPrime),当mPrime小于对列长度的一半时,则从前往后依次遍历找到清除的元素;当mPrime大于对列长度的一半时,从后往前遍历找到清除的元素。
    • public static void pass2(int m, int n){
              ArrayList<Integer> list = new ArrayList<Integer>();
              for(int i = 1; i <= n; i++)
                  list.add(i);
              ListIterator<Integer> iter = list.listIterator();
              int left = n;    //剩下的人数
              int item = 0;    //the out number
              for(int i= 1; i < n; i++){
                  int mPrime = m % left;
                  if(mPrime < left/2){     //如果当前的偏移量小于list长度的一半时
                      if(iter.hasNext())
                          item = iter.next();
                      for(int j = 0; j < mPrime; j++){
                          if(!iter.hasNext())
                              iter= list.listIterator();
                          item = iter.next();
                      }
                  }
                  else{  //当偏移量大于list长度的一半时,从后往前找
                      for(int j = 0; j< left - mPrime; j++){
                          if(!iter.hasPrevious())
                              iter = list.listIterator(list.size());
                          item = iter.previous();
                      }
                  }
                  System.out.println("out number is " + item);
                  iter.remove();
                  if(!iter.hasNext())    //有可能下一次循环mPrime就会小于left的一半
                      iter = list.listIterator();
                  left--;
              }
          }
  • 总结

当M,N较大时,则第二种方法时间效率更高,实现表明,当N=100000,M=9000时(省略的两个算法中的syso语句),方法一个执行时间是30713ms,而第二种方法的执行时间仅为4891ms,M越大时方法一的时间效率会更差。

时间: 2024-12-09 22:36:17

Josephus环问题的相关文章

笔试算法题(11):Josephus环 &amp; Fibonacci序列

出题:Josephus Cycle,约瑟夫环问题.k个数字连成一个环,第一个数字为1.首先从1开始计数删除第m个数字:然后从上次被删除的数字的下一个数字开始计数,删除第m个数字:重复进行第二步直到只剩下一个数字:输出最后剩下的一个数字: 分析: 解法1:考虑到问题的特殊性,可以使用哑元素表示删除的元素从而避免由于删除元素带来的额外操作,所以链表实现的话不用考虑删除操作,数组实现的话不用考虑内存移动操作.当然完全可以不适用哑元素,对于链表而言可以节省查找时间,数组的话需要增加数组元素的平移开销:

Josephus环类问题,java实现

写出一个双向的循环链表,弄一个计数器,我定义的是到三的时候,自动删除当前节点,很简单. package Com; import java.util.Scanner; /* * 约瑟夫环问题,有n个人组成的圈,数到3的那个人出列,下个人继续从一开始 */ public class Josephus { public static void main(String[] args) { Scanner s = new Scanner(System.in); int n = Integer.parseI

第一个只出现一次的字符,josephus环,最大子数组和

#include<stdio.h> #include<stdlib.h> #include<string.h> #define MAXINT 0x7fffffff #define MININT 0X80000000 //字符串中第一个只出现一次的字符 char firstSingle(char *str) { int a[255]; memset(a, 0, 255 * sizeof(int)); char *p = str; while (*p != '\0'){ +

Josephus环的四种解法(约瑟夫环)

约瑟夫环 约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列.通常解决这类问题时我们把编号从0~n-1,最后结果+1即为原问题的解引用别人的一个图:直观说明问题 分析: 第一步:从1开始报数为3的时候就删除3号结点第二步:从4号结点开始报数,当为3的时候删除6号结点:第三步:从7号结点开始报数,当为3的时候

xdu_1009: Josephus环的复仇(线段树)

题目链接 题意不难理解,解法具体看代码及注释吧.. #include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxn=2e5+7; int n,k; int ql,qr; int pos; //当前要出队的人在剩余人中排在第几靠左的地方 struct node { int l,r; int pre; //记录区间中剩余的最靠右的人在所有剩余人中的位置 int lz; //lazy标记 } a[ma

1139 约瑟夫环问题

1139 约瑟夫环问题 时间限制:500MS  内存限制:65536K提交次数:157 通过次数:79 题型: 编程题   语言: G++;GCC Description 约瑟夫(josephus)环是这样的:假设有n个小孩围坐成一个圆圈,并从1开始依次给每个小孩编上号码.老师指定从第s位小孩起从1开始报数,当数到m时,对应的小孩出列,依次重复,问最后留下的小孩是第几个小孩?例如:总共有6个小孩,围成一圈,从第一个小孩开始,每次数2个小孩,则游戏情况如下: 小孩序号:1,2,3,4,5,6 离开

低刨讼平用骨耐醚褪醒劝GE2g

如果说下水道是一个城市的良心,那么厕所就是一座城市的门脸. 很多年前,人们就发现一个有意思的现象:一个城市的文明程度可以从其厕所的卫生程度得出,厕所越干净.城市越发达.但直到今天,大数据无法解释这背后的原因:究竟是城市发达了厕所自然就干净了,还是厕所干净了也会促进城市的发展. 对于这样「先有鸡还是先有蛋的问题」,还需要大数据加传统民调和小数据的结合才有肯能找到答案. 01 谷歌为何只猜对了一次? 感谢从维克托·迈尔-舍恩伯格到马云的孜孜不倦地布道,大数据现在几乎成了全球先进生产力的标志,而且几乎

约瑟夫环问题(Josephus)

[问题描述] 用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出.写出C程序.(约瑟夫环问题 Josephus) [解题思路] 构建一个循环链表,每个结点的编号为1,2,......,n.每次从当前位置向前移动m-1步,然后删除这个结点. [C程序代码] #include <stdio.h> #include <stdlib.h> typedef struct node { int num; struct node *next; }node; node *

习题3.10 约瑟夫环 josephus问题

/* assume a header */ /* 双向循环链表 */ struct Node; typedef struct Node * PtrToNode; typedef PtrToNode List; typedef PtrToNode position; struct Node{ PtrToNode Previous; PtrToNode Next; int Ele; }; /* 删除双向循环链表中的元素例程 */ Position Delete( Position p ) { Pos