【牛客网刷题(数据结构)】:环形链表的约瑟夫问题

描述

编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数,报到 m 的人离开。

下一个人继续从 1 开始报数。

n-1 轮结束以后,只剩下一个人,问最后留下的这个人编号是多少?

O(n)

示例1

好环形链表的约瑟夫问题是一个经典的问题,它的描述如下:有n个人围成一圈,从第一个人开始报数,报到m的人出圈,剩下的人继续从1开始报数,直到剩下最后一个人。现在给定n和m,求最后剩下的人的编号

这个问题可以使用环形链表来解决。具体来说,我们可以先构建一个包含n个节点的环形链表,然后从第一个节点开始遍历链表,每次遍历m个节点,将第m个节点从链表中删除。重复这个过程直到链表中只剩下一个节点为止,这个节点就是最后剩下的节点

输入:

5,2

返回值:

3

说明:

开始5个人 1,2,3,4,5 ,从1开始报数,1->1,2->2编号为2的人离开

1,3,4,5,从3开始报数,3->1,4->2编号为4的人离开

1,3,5,从5开始报数,5->1,1->2编号为1的人离开

3,5,从3开始报数,3->1,5->2编号为5的人离开

最后留下人的编号是3

示例2

输入:

1,1

复制

返回值:

1

关于环形链表的约瑟夫问题,具体思路如下:

首先创建一个环形链表,链表中每个节点代表一个人,节点编号从1开始递增。

然后从第一个节点开始报数,每报到第m个人就将该节点从链表中删除。

删除节点后,从下一个节点重新开始报数,重复上述步骤,直到只剩下一个节点为止。

下面是C++代码实现:

cpp 复制代码
#include <iostream>
using namespace std;

struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(NULL) {}
};

int josephus(int n, int m) {
    ListNode* head = new ListNode(1);
    ListNode* cur = head;
    for (int i = 2; i <= n; i++) {
        cur->next = new ListNode(i);
        cur = cur->next;
    }
    cur->next = head; // 将链表首尾相连

    while (cur->next != cur) { // 只剩下一个节点时结束循环
        for (int i = 1; i < m; i++) {
            cur = cur->next;
        }
        ListNode* tmp = cur->next;
        cur->next = tmp->next;
        delete tmp;
    }
    int ans = cur->val;
    delete cur;
    return ans;
}

int main() {
    int n, m;
    cin >> n >> m;
    cout << josephus(n, m) << endl;
    return 0;
}

C语言代码实现

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

// 定义链表节点结构体
typedef struct Node {
    int num;            // 节点编号
    struct Node *next;  // 指向下一个节点的指针
} Node;

// 创建环形链表
Node *createList(int n) {
    Node *head = NULL, *tail = NULL;
    for (int i = 1; i <= n; i++) {
        Node *p = (Node *)malloc(sizeof(Node));
        p->num = i;
        if (head == NULL) {
            head = p;
        } else {
            tail->next = p;
        }
        tail = p;
    }
    tail->next = head;  // 将尾节点指向头节点,形成环形链表
    return head;
}

// 约瑟夫问题求解
void josephus(Node *head, int m) {
    Node *p = head, *prev = NULL;
    while (p->next != p) {  // 只剩下一个节点时结束循环
        for (int i = 1; i < m; i++) {
            prev = p;
            p = p->next;
        }
        prev->next = p->next;  // 删除节点
        printf("%d ", p->num);
        free(p);
        p = prev->next;  // 从下一个节点重新开始报数
    }
    printf("%d\n", p->num);
    free(p);
}

int main() {
    int n, m;
    printf("请输入总人数n和报数m:");
    scanf("%d%d", &n, &m);
    Node *head = createList(n);
    josephus(head, m);
    return 0;
}
相关推荐
CSharp精选营4 天前
关系型 vs 非关系型:从原理到选型,一文搞定数据库核心分类
数据结构·nosql·关系型数据库·非关系型数据库·技术选型
To_OC5 天前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode
To_OC5 天前
LC 208 实现 Trie 前缀树:曾被名字劝退,写完发现是送分题
javascript·算法·leetcode
To_OC6 天前
LC 994 腐烂的橘子:人人都说是 BFS 入门题,我却写了三遍才过
javascript·算法·leetcode
To_OC6 天前
LC 200 岛屿数量:经典 DFS 入门题,我第一次写居然连方向都搞错了
javascript·算法·leetcode
To_OC7 天前
LC 128 最长连续序列:别上来就排序,O (n) 解法才是这题的灵魂
javascript·算法·leetcode
刘马想放假8 天前
Modbus 全栈技术解析:TCP、RTU、ASCII、RTU over TCP
数据结构·网络协议
北域码匠8 天前
冒泡排序太慢?鸡尾酒排序双向优化,原生 C# 零第三方库完整代码
数据结构·排序算法·泛型·c# 算法·鸡尾酒排序·原生 c# 开发·冒泡排序优化·嵌入式算法
To_OC9 天前
LC 49 字母异位词分组:想到哈希表很简单,选对 key 才是精髓
javascript·算法·leetcode
To_OC10 天前
LC 1 两数之和:面试第一道必考题,暴力解法直接被面试官 pass
javascript·算法·leetcode