首页 > 试题广场 >

环形链表的约瑟夫问题

[编程题]环形链表的约瑟夫问题
  • 热度指数:3154 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
据说著名犹太历史学家 Josephus 有过以下故事:在罗马人占领乔塔帕特后,39 个犹太人与 Josephus 及他的朋友躲到一个洞中,39 个犹太人决定宁愿死也不要被敌人抓到,于是决定了一种自杀方式,41 个人排成一个圆圈,由第 1 个人开始报数,报数到 3 的人就自杀,然后再由下一个人重新报 1,报数到 3 的人再自杀,这样依次下去,直到剩下最后一个人时,那个人可以自由选择自己的命运。这就是著名的约瑟夫问题。现在请用单向环形链表得出最终存活的人的编号。


输入描述:
一行两个整数 n 和 m, n 表示环形链表的长度, m 表示每次报数到 m 就自杀。


输出描述:
输出最后存活下来的人编号(编号从1开始到n)
示例1

输入

5 2

输出

3

备注:
运用队列:
#include <iostream>
#include <queue>

using namespace std;

queue<int> q;

int main(){
	int n, m;
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		q.push(i);
	}
	while(q.size() != 1){
		//m-1个人重新入队
		for(int i = 1; i < m; i++){
			q.push(q.front());
			q.pop();
		}
		q.pop();
	}
	cout << q.front() << "\n";
	return 0;
}
或者运用向量
#include <iostream>
#include <queue>
#include <vector>

using namespace std;

queue<int> q;
vector<int> v;

int main(){
	int n, m, i;
	cin >> n >> m;
	for(i = 1; i <= n; i++){
		v.push_back(i);
	}
	i = 0;
	while(!v.empty()){
		i = (i + m) % v.size();
		if(i == 0){
			q.push(v[v.size() - 1]);
			v.erase(v.end() - 1);
			i = v.size();
		}else{
			q.push(v[i - 1]);
			v.erase(v.begin() + i - 1);
			i -= 1;
		}
	}
	cout << q.back() << "\n"; 
	//清空队列和向量
	v.clear();
	v.shrink_to_fit();
	while(!q.empty()){
		q.pop();
	}
	return 0;
}



编辑于 2020-02-26 19:11:05 回复(0)
## n总人数 m报道数 k第几次剔除的
新旧数组变化连接
import java.util.Scanner;
public class Main{
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        System.out.println(getResult(n,m,n)+1);     
    }
    public static int getResult(int n,int m,int k) {
        if(n==1)return (n+m-1)%n;
        else return (getResult(n-1,m,k-1)+m)%n;
    }
}
发表于 2019-10-16 19:20:24 回复(0)
Java 解
时间:O(N^2)
空间:O(N)

import java.util.*;
public class Main {
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        while(input.hasNextInt()){
            int n = input.nextInt();
            int m = input.nextInt();
            int ans = getResult(n,m);

            System.out.println( ans );
        }
        input.close();
    }
    /**
     * 这是一个仿真程序
     */
    public static int getResult(int n, int m) {
        // write code here
        boolean[] r = new boolean[n];
        //record序列,r[i]用于记录第i个人是否已被淘汰,初始为false
        int pass = 0;    //已淘汰数
        int i = 0;
        int sum = 0;    //目前数到第sum个
        while(pass < n){
            if(i == n)
                i = 0;
            if(r[i] == false){    //第i人未淘汰
                sum++;
                if(sum == m){    //淘汰当前报数者
                    pass++;
                    r[i] = true;
                    sum = 0;    //1人出局后,重新报数
                    continue;
                }
                i++;
            }else    //第i人淘汰
                i++;
        }
        return i+1;    //自然序比程序下标大1.
    }
}


发表于 2019-10-16 11:43:16 回复(0)

#include <iostream>
#include <vector>
using namespace std;
int m, n;
struct node {
    int val;
    node* next;
};
node* fun(node* head, int m) {
    if (head == nullptr || head->next == head || m < 1)
        return head;
    node* last = head;
    while (last->next != head)
        last = last->next;
    int cnt = 0;
    while (head != last) {
        if (++cnt == m) {
            last->next = head->next;
            cnt = 0;
        }
        else {
            last = last->next;
        }
        head = last->next;
    }
    return head;
}


int main() {
    cin >> n >> m;
    node* head = nullptr;
    node* p = nullptr;
    for (int i = 1; i <= n; i++) {
        if (head == nullptr) {
            head = new node;
            head->val = i;
            head->next = nullptr;
            p = head;
            continue;
        }
        p->next = new node;
        p = p->next;
        p->val = i;
        p->next = nullptr;
    }
    p->next = head;
    node* node1 = fun(head, m);
    cout << node1->val << endl;
    system("pause");
    return 0;
}

发表于 2019-08-02 22:43:14 回复(0)
#include <bits/stdc++.h>
using namespace std;

int main(){
    int n, m, s=0;
    scanf("%d%d", &n, &m);
    for(int i=2;i<=n;i++)
        s = (s+m)%i;
    printf("%d\n", s+1);
    return 0;
}

发表于 2020-06-16 19:51:21 回复(1)
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,m,res=0;
    cin>>n>>m;
    for(int i = 2; i <= n; i++)
        res = (res + m)%i;
    cout<<(res + 1);
    return 0;
}

发表于 2019-09-16 19:34:51 回复(0)
/*简单模拟*/
#include <stdio.h>
#include <stdlib.h>

typedef struct nd {
    int val;
    struct nd *next;
} node;

node *create_node(int val) {
    node *n = (node *) malloc(sizeof(node));
    n->val = val;
    n->next = NULL;
    return n;
}

node *josephus_kill(node *head, int m) {
    int tmp = 1;
    node *pre, *cur = head;
    while (cur->next != head)
        cur = cur->next;
    pre = cur;
    cur = head;
    while (cur->next != cur) {
        if (tmp == m) {
            tmp = 1;
            pre->next = cur->next;
            free(cur);
            cur = pre->next;
            continue;
        }
        pre = cur;
        cur = cur->next;
        tmp++;
    }
    return cur;
}

int main(void) {
    int n, m;
    scanf("%d%d", &n, &m);
    
    node dh = {0, NULL}, *cur = &dh;
    for (int i = 0; i < n; i++) {
        cur->next = create_node(i + 1);
        cur = cur->next;
    }
    cur->next = dh.next;
    cur = josephus_kill(dh.next, m);
    printf("%d\n", cur->val);
    return 0;
}

发表于 2022-05-08 20:05:57 回复(0)
这个题如果不是非常经典并且作为教学问题的话,是真心难想。我们考察一下相邻两次报数的差异如下:
长度为i:1    2    3 ...... m-1    m    m+1    m+2 ...... i-1      i
                                                ×(m被移除掉后)
长度i-1:           .....       i-1             1          2            i-m-1  i-m
我们看看对应关系
   新    —— 
i-m+1 —— 1
i-m+2 —— 2
      ......
   1     —— m+1
   2     —— m+2
      ......
i-m-1 —— i-1
 i-m   —— i
画出函数图像发现就是函数y=x%i经过平移后的图像

根据上加下减左加右减的原理,可以得到本轮报数是x的情况下上一轮的报数y=(x+m-1)%i+1。而游戏进行到最后时,幸存者的报数是1,于是我们可以通过这个公式倒推回有n个人时的报数。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

class ListNode {
    public int val;
    public ListNode next;
    public ListNode(int val) {
        this.val = val;
    }
}

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] params = br.readLine().split(" ");
        int n = Integer.parseInt(params[0]), m = Integer.parseInt(params[1]);
        ListNode head = new ListNode(1);
        ListNode cur = head;
        for(int i = 2; i <= n; i++){
            cur.next = new ListNode(i);
            cur = cur.next;
        }
        int live = 1;     // 长度为1时,剩下节点的编号
        // 倒推得到长度为n时它的编号
        for(int i = 2; i <= n; i++) live = (live + m - 1) % i + 1;
        cur = head;
        for(int i = 1; i < live; i++) cur = cur.next;
        System.out.println(cur.val);
    }
}
直接用链表模拟的话每报数m次就删除一个节点,一共要删除n-1个节点,因此时间复杂度为O(mn);而用公式倒推的话需要调用n-1次时间复杂度为O(1)的公式,整体时间复杂度为O(n)。
编辑于 2021-12-02 16:52:19 回复(0)
#include <bits/stdc++.h>
using namespace std;

int main(){
    int n, m;
    cin >> n >> m;
    int alive = 0;
    for(int i = 2; i <= n; i++){
        alive = (alive + m)%i;
    }
    cout << alive+1 <<endl;
    return 0;
}

发表于 2020-05-11 10:02:55 回复(1)
#include <iostream>
#include <queue>
using namespace std;

int main() {
    int n, m, bh = 1, cur;
    cin >> n >> m;
    queue<int> q;
    for (int i = 0; i < n; i++)
        q.push(i);
    while (q.empty() == false) {
        if (bh == m) {
            bh = 1; //保号为m 则出队 不用插入队尾 且重置报号
            cur = q.front();
            q.pop();

        } else {
            //如果报号不为m 则出队后插入队尾
            cur = q.front();
            q.pop();
            q.push(cur);
            bh++;
        }

    }
    cout << cur + 1 << endl;
}

发表于 2024-02-04 16:30:30 回复(0)
#include <stdio.h>
#include<stdlib.h>
typedef int SDataType;
typedef struct StackNode {
    SDataType val;
    struct StackNode* next;
} StackNode;
typedef struct Stack {
    StackNode* head;
    int size;
} Stack;
//创建节点
StackNode* CreateNode(SDataType elemest) {
    //创建
    StackNode* new = (StackNode*)malloc(sizeof(StackNode));
    if (new == NULL) {
        perror("CreateNode");
        return NULL;
    }
    new->next = NULL;
    new->val = elemest;
    return  new;
}
//初始化
void StackInit(Stack* root) {
    root->head = NULL;
    root->size = 0;
}
//插入
void StackPush(StackNode** head, SDataType elemest) {
    //创建
    StackNode* newnode = CreateNode(elemest);
    if (*head == NULL) {
        *head = newnode;
        newnode->next = *head;
    } else {
        StackNode* tail = *head;
        while (tail->next != *head) {
            tail = tail->next;
        }
        tail->next = newnode;
        newnode->next = *head;
    }
}
//删除
void StackPop(StackNode** head, StackNode* node) {
    if (head == NULL)
        return;
    if (*head == node) {
        StackNode* tail = (*head)->next;
        while (tail->next != *head) {
            tail = tail->next;
        }
        *head = (*head)->next;
        tail->next = *head;
        free(node);
        return;
    }
    StackNode* tail = (*head)->next;
    while (tail != *head) {
        if (tail->next == node)
            break;
        tail = tail->next;
    }
    tail->next = node->next;
    free(node);

}
//释放
void StackDestoy(StackNode* head) {
    StackNode* tail = head->next;
    while (tail != head) {
        StackNode* p = tail->next;
        free(tail);
        tail = p;
    }
}
int main() {
    //
    Stack* root = (Stack*)malloc(sizeof(Stack));
    //初始化
    StackInit(root);
    int n = 0;
    scanf("%d", &n);
    int i = 1;
    for (i = 1; i <= n; i++) {
        //插入
        StackPush(&(root->head), i);
        root->size++;

    }
    //
    int m = 0;
    scanf("%d", &m);
    int count = 1;
    StackNode* tail = root->head;
    while (root->size != 1) {
        if (count == m) {
            root->size--;
            count = 0;
            //删除
            StackNode* p = tail->next;
            StackPop(&(root->head), tail);
            tail = p;
            count++;
        } else {
            count++;
            tail = tail->next;
        }
    }
    printf("%d", root->head->val);
    StackDestoy(root->head);
    free(root);
    return 0;
}
编辑于 2023-12-22 16:17:09 回复(0)
import java.util.*;
class node{
        int val;
        node next;
        public node(){};
        public node(int val) {
            this.val = val;
        }
}

public class Code61_yuesefuProblem {
    public static void main(String[] args) {
        node ans = new node();
        node index = ans;
        Scanner in = new Scanner(System.in);
        node mynode = new node();
        int n = in.nextInt();
         int m=in.nextInt();
        for (int i = 1; i <= n; i++) {
            node temp = new node(i);
            index.next = temp;
            index = index.next;
        }
        index.next = ans;
        int ch=n;
        int count=1;
        node p=ans;
        node q=ans.next;
        while(ch!=1) {
            if (count == m &&q.val!=0) {
                //System.out.println("所删除的节点"+q.val);
                q = q.next;
                p.next = q;
                count = 1;
                ch--;
            } else if (count != m &&q.val!=0) {
                p = p.next;
                q = q.next;
                count++;
            }
            else{
                p=p.next;
                q=q.next;
            }
        }
        node test=ans.next;
        while(true) {
            if(test.val!=0){
                System.out.println(test.val);
                break;
            }
            test=test.next;
        }
            }
        }

大概思路 1.就是循环赋值然后附完值后让指针指向头形成单循环链表 2.然后删除用的基本操作遇到对应的删除节点时前指针先走一步然后后指针指向他删除该指针并且计数器count置为1,如果不是的话就俩指针各走一步然后count++ 3.对了我加了一个q.val!=0是因为我创建的链表是带头节点的方便操作但是头节点不参与循环所以到他的时候基数器不参与任何变化
编辑于 2023-03-10 15:20:21 回复(0)
#include<iostream>
#include<cstdio>
#include<queue>

using namespace std;

int main()
{
    int m,n;
    scanf("%d %d",&n,&m);
    queue<int>children;
    
    for(int i=0;i<n;i++)
    {
        children.push(i);
    }
    while(children.size()!=1)
    {
        for(int j=0;j<m-1;j++)
        {
            children.push(children.front());
            children.pop() ;
        }
        children.pop();
    }
    printf("%d",children.front());
    return 0;
}
发表于 2023-02-15 19:22:47 回复(0)
数学解法:
#include<iostream>  
using namespace std;  
int main() {  //数学解法
    int n,m;  
    cin>>n>>m;  
    int r=0;  
    for(int i=2;i<=n;i++){  
        r=(r+m)%i;  //上一轮编号
    }  
    cout<<r+1;
    return 0;
}  
编辑于 2021-10-11 16:02:51 回复(2)
今天递归爆栈了,不用递归就ok
let [n,m]=readline().split(' ').map(item=>parseInt(item))
const listNode=function(val){
    this.val=val
    this.next=null
}

let head=new listNode(-1)
let p=head
for(let i=0;i<n;i++){
    const node=new listNode(i+1)
    p.next=node
    p=node
}
p.next=head.next
head.next=null
let q=p
p=q.next

// 计算每轮生存节点
// const getLive=function(n,m){
//     if(n==1) return 1
//     else  return (getLive(n-1,m)+m-1)%n+1
// }

// const compute=function(head,m){
//     if(head==null||head.next==head||m<1) return head
//     let count=getLive(n,m)
//     while(count>1){
//         head=head.next
//         count--
//     }
//    console.log(head.val) 
// }
const compute=function(){
    if(head==null||head.next==head||m<1) return head
      let count=1
    for(let i=2;i<=n;i++){
        count=(count+m-1)%i+1
    }
  
//     while(count>1){
//         head=head.next
//         count--
//     }
   console.log(count) 
}
compute()




编辑于 2021-07-02 14:07:25 回复(0)
n,m=map(int,input().split())
l=list(range(1,n+1))
res=0
#模拟删除过程
#每次去掉一个人
#相当于i+1
for i in range(1,n+1):
    res=(res+m)%i
print(res+1)

发表于 2021-06-30 09:47:17 回复(0)
#include<iostream>
#include<list>
using namespace std;
int main(){
    int N;
    cin>>N;
    int n;
    cin>>n;
    list<int> nums;
    for(int i=1;i<=N;i++){
        nums.push_back(i);
        //cout<<i;
    }
    auto cur = nums.begin();
    while(nums.size()>1){
        for(int i=0;i<n-1;i++){
            cur++;
            if(cur==nums.end()){
                cur=nums.begin();
            }
        }
        auto next = ++cur;
        if(next==nums.end()){
            next = nums.begin();
        }
        cur--;
        nums.erase(cur);
        cur = next;
    }
    //cout<<*nums.begin();
    cout<<nums.front();
    return 0;
}
发表于 2020-07-23 20:26:40 回复(0)
class ListNode:
    def __init__(self, x):
        # 保存节点的数据
        self.val = x
        # 下一个节点的地址
        self.next = None

def create(length):
    head_node = ListNode(1)
    res = head_node
    for i in range(2, length+1):
        head_node.next = ListNode(i)
        head_node = head_node.next
    head_node.next = res
    return res

def josephus_kill_1(head, m):
    '''
    返回最终剩下的那个结点
    时间复杂度为o(m*len(ringlinkedlist))
    '''
    p = head
    if not p:
        return -1
    count = 1
    while p.next != p:
        count+=1
        if count==m:
            temp = p.next
            p.next = temp.next
            del temp
            count = 1
        p = p.next

    print(p.val)
        

if __name__=="__main__":
    n, m = map(int,input().split())
    phead = create(n)
    josephus_kill_1(phead,m)
运行超时:您的程序未能在规定时间内运行结束,请检查是否循环有错或算法复杂度过大。点击对比用例标准输出与你的输出
case通过率为66.67%
请问怎么解决?牛客上刷题总是遇到这个问题。
发表于 2020-07-22 18:06:02 回复(0)
import java.util.*;

public class Main {
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        //初始化队列
        Queue<Integer> queue = new LinkedList<>();
        //向队列中添加元素
        for (int i=1; i<=n; i++){
            queue.offer(i);
        }
        //判断队列的元素是否为1
        while (queue.size() != 1){
            //向队列尾部添加队列前m-1个元素,同时删除队列前m-1个元素
            for (int i=1; i<m; i++){
                queue.offer(queue.poll());
            }
            //删除队列第m个元素
            queue.poll();
        }
        //如果队列只有1个元素,输出元素,不删除队列元素
        System.out.println(queue.peek());
    }
}

发表于 2020-06-03 10:11:58 回复(0)
list_node* run(list_node* head,int n,int m){
    list_node* curr = head;
    list_node* pre = head;
    if (head->next == NULL){
        return NULL;
    }
    for (int i = 1;i < n;i++){
        pre = pre->next;
    }
    while (curr->next != pre){
        for(int i = 1;i < m;i++){
            pre = pre->next;
            curr = curr->next;
           
        }
       
        pre->next = curr->next;
        curr = curr->next;
    }
    if (m % 2 == 0){
        return curr;
    }
    else{
        return pre;
    }
}
发表于 2020-03-31 17:58:04 回复(0)

问题信息

上传者:小小
难度:
22条回答 6308浏览

热门推荐

通过挑战的用户

查看代码