算法之约瑟夫环问题:有n个人排成一列或是一圈,从编号为k的人开始报数,数到m的那个人出列。(Java)



约瑟夫环问题

约瑟夫环描述:约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。



1.题目

约瑟夫环问题,输入人数n,第m个人出列,最后一个人的编号是多少?



例子:
输入:5 3
输出:4
例子



2.解析

1.先把这n个人按顺序加进LinkedList。选择链表是因为链表在进行大量数据插入删除的时候会比顺序表ArrayList的效率要高,而ArrayList在查找的时候效率会相对高。

2.从索引0开始报数,到索引m-1的时候,也就是第m个人出列。

index = m - 1;  //初始化,index指向第一个出列的人
linkedList.remove(index);

3.排在这个人后面的人都向前移了一位,也就是如4的索引原来是3的,在3出列之后,队伍里还有[1,2,4,5]四个人,这时候4的索引变成了3-1=2,5的索引变成了4-1=3。所以新的索引就要从出列的人重新开始数,再次数到m-1。再次执行上面的删除操作。

index += m - 1;

4.我们还会发现一个问题,就是index一直加的话,岂不是会数组越界了?这时候该怎么办呢?怎么样才能让一个链表排出一个圈的效果呢?
我们可以用 索引 % 链表长度 进行取余操作

5.最后,我们总结一下。需要初始化的索引,与循环体内的代码如下:

index = m - 1;  //初始化,index指向第一个出列的人

while(linkedList != 0){
    index %= linkedList.size();
    linkedList.remove(index);
    index += m - 1;
}


3.总代码

private int josephRingMethod(int n, int m){

    LinkedList<Integer> linkedList = new LinkedList();
    for (int i = 1; i <= n; i++) {
        linkedList.add(i);
    }

    int index = m - 1;  //初始化,index指向第一个出列的人
    while (linkedList.size() != 0){
        System.out.println(linkedList);
        //用 **索引 % 链表长度** 进行取余操作,避免下标越界
        index %= linkedList.size(); 

        //出列
        System.out.println(linkedList.get(index) + "号出列");
        linkedList.remove(index);

        //返回最后剩下的人的编号
        if (linkedList.size() == 1){
            return linkedList.get(0);
        }
        //新的索引就要从出列的人重新开始数,再次数到m-1。
        index += m - 1;
    }

    return -1;
}


  • 7
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 这是一个利用队列实现的约瑟夫环问题:已知n个人(以编号1,2,3……n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数数到m的那个人出列;他的下一个人又从1开始报数数到m的那个人出列;依次类推,直到剩余最后一个人。根据编号为k的人开始报数,求出最后一个出列的人的编号。 针对这个问题,可以建立一个初始序列为1~n的队列,然后依次按规则将队列首端的数字移至队列尾端,直到队列中只剩下最后一个数字为止,即为最后一个出列的人的编号。 ### 回答2: 这道题目可以通过队列来实现,可以按照以下步骤进行: 1. 将所有人的编号加入队列中。 2. 从队列中弹出第k个人,将其从队列中删除,并将其保存到一个结果列表中。 3. 将队列中的所有人的编号向后移动k-1个位置,使得下一个出圈的人成为队首。 4. 重复步骤2和步骤3,直到队列中只剩下一个人为止。 具体实现时,我们可以用Python语言来实现,代码如下: ```python def josephus(n, k): queue = list(range(1, n+1)) result = [] while queue: # 第k个人出圈 index = (k-1) % len(queue) result.append(queue.pop(index)) # 向后移动k-1个位置 queue = queue[index:] + queue[:index] return result ``` 其中,n表示总人数,k表示开始报数的人的编号。在每一次循环中,我们先找到第k个人,将其从队列中删除并添加到结果列表中,然后向后移动k-1个位置,使得下一个出圈的人成为队首。最后,返回结果列表即可。 这个算法的时间复杂度为O(n^2),因为每次需要移动k-1个位置,总共需要移动n-1次,所以总共需要执行n(n-1)次。如果使用双向循环链表的数据结构,可以将移动操作的时间复杂度优化到O(1),从而实现更快的算法。 ### 回答3: 利用队列实现约瑟夫环问题非常简单,我们只需要定义一个队列,将所有的人依次加入到队列中,并依次出队即可。 具体实现步骤如下: 1.定义一个空队列,并将所有的人依次加入到队列中。 2.定义一个计数器变量count,初始化为1,表示从1开始报数。 3.定义一个循环,当队列的长度大于1时,就一直进行循环,直到只有一个人留下。 4.在循环中,每次从队列中出队一个人,然后将count加1。 5.如果count等于k,说明当前这个人需要出圈,直接舍弃不加入队列。 6.否则,将这个人加入到队列尾部,继续循环。 7.重复步骤4-6,直到只有一个人留下,那么这个人就是最后的幸存者。 代码实现如下: def josephus(n, k): # 定义一个空队列,并将所有的人依次加入到队列中 queue = [i for i in range(1, n+1)] # 定义一个计数器变量count,初始化为1 count = 1 # 循环队列,直到只有一个人留下 while len(queue) > 1: # 从队列中出队一个人 person = queue.pop(0) # 将count加1 count += 1 # 如果count等于k,说明当前这个人需要出圈 if count == k: count = 1 continue # 否则,将这个人加入到队列尾部 queue.append(person) # 最后剩下的那个人就是幸存者 return queue[0] # 测试 print(josephus(10, 3)) # 输出4

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值