约瑟夫环问题(带密码)

约瑟夫环问题(带密码)

编号为1,2,…,n的n个人按顺时针方向围坐一圈,每个人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m 值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直到所有人全部出列为止。例如,n=7,7个人的密码依次为:3,1,7,2,4,8,4,m的初值取6,则正确的出列顺序应为6,1,4,7,2,3,5

分析

约瑟夫环的大小是变化的,因此相应的结点也是变化的,使用链式存储结构可以动态的生成其中的结点,出列操作也非常简单。用单向循环链表模拟其出列顺序比较合适

解决

用结构指针描述每个人

struct Joseph {
    //约瑟夫环中某个人的序号
    int number;
    //约瑟夫环中某个人的密码
    int password;
    //约瑟夫环的下一个节点
    struct Joseph *next;
};

初始化约瑟夫环

Node *init(int n, int array[]) {
    int i = 1;
    //链表头结点
    Node *head;
    //链表当前节点
    Node *current;
    //链表下一个节点
    Node *next;
    current = static_cast<Node *>(malloc(sizeof(Node)));
    current->number = i;
    current->password = array[i - 1];
    head = current;
    for (i = 2; i <= n; ++i) {
        next = static_cast<Node *>(malloc(sizeof(Node)));
        next->number = i;
        next->password = array[i - 1];
        //连接链表与新创建的节点
        current->next = next;
        current = next;
    }
    //尾节点连接到头结点,形成循环链表
    current->next = head;
    return head;
}

报数出列

void function(Node *head, int len, int password) {
    //前一个节点
    Node *pre;
    //后一个节点
    Node *next;
    //要删除的节点
    Node *temp;
    //初始化
    next = head;
    for (int i = 1; i < len; ++i) {
        pre = next->next;
        next = next->next;
    }
    //遍历所有人
    for (int i = 1; i <= len; ++i) {
        //先将指针移动到出列的前一个
        for (int j = 1; j < password; ++j) {
            pre = pre->next;
        }
        //保存要删除的节点
        temp = pre->next;
        //下一个节点
        next = temp->next;
        //修改密码
        password = temp->password;
        //输出出列序号
        cout << temp->number << endl;
        //连接链表,去除中间节点
        pre->next = next;
        //释放中间节点
        free(temp);
    }
}

完整代码

#include <iostream>

using namespace std;

struct Joseph {
    //约瑟夫环中某个人的序号
    int number;
    //约瑟夫环中某个人的密码
    int password;
    //约瑟夫环的下一个节点
    struct Joseph *next;
};

typedef Joseph Node;

/**
 * 初始化约瑟夫环
 * @param n 成员数目
 * @param array 每个人的密码
 * @return 头结点
 */
Node *init(int n, int array[]) {
    int i = 1;
    //链表头结点
    Node *head;
    //链表当前节点
    Node *current;
    //链表下一个节点
    Node *next;
    current = static_cast<Node *>(malloc(sizeof(Node)));
    current->number = i;
    current->password = array[i - 1];
    head = current;
    for (i = 2; i <= n; ++i) {
        next = static_cast<Node *>(malloc(sizeof(Node)));
        next->number = i;
        next->password = array[i - 1];
        //连接链表与新创建的节点
        current->next = next;
        current = next;
    }
    //尾节点连接到头结点,形成循环链表
    current->next = head;
    return head;
}

/**
 * 遍历约瑟夫环,报数出列
 * @param head 头结点
 * @param len 约瑟夫环长度
 * @param password 初始密码
 */
void function(Node *head, int len, int password) {
    //前一个节点
    Node *pre;
    //后一个节点
    Node *next;
    //要删除的节点
    Node *temp;
    //初始化
    next = head;
    for (int i = 1; i < len; ++i) {
        pre = next->next;
        next = next->next;
    }
    //遍历所有人
    for (int i = 1; i <= len; ++i) {
        //先将指针移动到出列的前一个
        for (int j = 1; j < password; ++j) {
            pre = pre->next;
        }
        //保存要删除的节点
        temp = pre->next;
        //下一个节点
        next = temp->next;
        //修改密码
        password = temp->password;
        //输出出列序号
        cout << temp->number << endl;
        //连接链表,去除中间节点
        pre->next = next;
        //释放中间节点
        free(temp);
    }
}

int main() {
    //环的长度
    int len;
    cout << "输入约瑟夫环的长度:";
    cin >> len;
    int array[len];
    cout << "请输入m的初始值 m:";
    int m;
    cin >> m;
    cout << "请输入每个人的密码: " << endl;
    for (int i = 0; i < len; ++i) {
        cin >> array[i];
    }
//    int len = 7;
//    int array[] = {3, 1, 7, 2, 4, 8, 4};
//    int m = 6;
    //创建约瑟夫环
    Node *head = init(len, array);
    function(head, len, m);
    return 0;
}
  • 12
    点赞
  • 65
    收藏
  • 打赏
    打赏
  • 13
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:精致技术 设计师:CSDN官方博客 返回首页
评论 13

打赏作者

凡尘code

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值