用循环链表求解约瑟夫问题

  约瑟夫问题的提法:n个人围成一个圆圈,首先第1个人从1开始,一个人一个人的顺时针报数,报到第m个人,令其出列;然后再从下一个人开始,从1顺时针报数,报到第m个人,再令其出列,…,如此下去,直到圆圈中只剩一个人为止,此人即为优胜者。

  例如  n = 8   m = 3

  

  

  该问题老师让我们在带头节点的单循环链表不带头节点的单循环链表双向循环链表静态循环链表中四选其一实现,我看到问题后第一反应选了带头节点单循环链表,以为这样可以统一空表和非空表的操作,事实上在这个问题中并不需要考虑这些,不过好在四种方式实现起来差不多。

  这里给出我用带头节点的单循环链表求解约瑟夫问题的源码:

#include <iostream>
using namespace std;

template <class T>
struct CircLinkNode            // 循环链表结点定义
{
    T data;                    // 数据域
    CircLinkNode<T>* link;    // 指针域
    CircLinkNode(CircLinkNode<T>* ptr = NULL)    // 无参构造函数(带默认参数)
    {
        link = ptr;
    }
    CircLinkNode(T d, CircLinkNode<T>* ptr = NULL)
    {
        data = d;
        link = ptr;
    }
};

template <class T>
class CircList     // 循环链表类定义
{
private:
    CircLinkNode<T>* first;                // 头指针
public:
    CircList()                            // 构造函数
    {
        first = new CircLinkNode<T>(0);    // 附加头结点的值为0
        first->link = first;            // 循环链表最后一个结点要指向头结点
    }
    CircLinkNode<T>* Locate(int i);        // 定位第i个结点
    bool Insert(int i, T x);            // 在第i个结点后插入x
};

template<class T>
CircLinkNode<T>* CircList<T>::Locate(int i)    // 定位第i个结点
{
    if (i < 0)                            // i值不合理
        return NULL;
    if (i == 0)                            // 定位的是头结点
        return first;
    CircLinkNode<T>* current = first;
    if (current->link == first)            // 循环链表为空
        return NULL;
    int count = 0;
    while (count < i)                    // 偱链找第i个结点
    {
        current = current->link;
        if (current != first)            // 计数规避附加头结点
        {
            count++;
        }
    }
    return current;
}

template<class T>
bool CircList<T>::Insert(int i, T x)    // 在第i个结点后插入新结点
{
    CircLinkNode<T>* current = Locate(i);
    if (current == NULL)
        return false;                    // 插入不成功
    CircLinkNode<T>* newNode = new CircLinkNode<T>(x);
    if (newNode == NULL)
    {
        cerr << "存储分配错误!" << endl;
        exit(1);
    }
    newNode->link = current->link;
    current->link = newNode;
    return true;                        // 插入成功
}

template <class T>
void Josephus(CircList<T>& Js, int n, int m)
{
    CircLinkNode<T> *p = Js.Locate(1);// p初始化指向第一个人
    CircLinkNode<T>    *pre = NULL;
    for (int i = 1; i < n; i++) // 执行n-1次
    {
        for (int j = 1; j < m; j++) // 数m-1个人
        {
            pre = p;
            p = p->link;
            if (p == Js.Locate(0))  // 计数忽略附加头结点
            {
                j--;
            }
        }
        cout <<"第"<<i<< "轮出列的人是" << p->data << endl;
        pre->link = p->link;
        delete p;
        p = pre->link;
        if (p == Js.Locate(0))
            p = p->link;
    }
    cout << "最后的优胜者是" << p->data << endl;
};

int main()
{
    CircList<int> clist;        // 建立带头结点的单循环链表
    int person_num, cycle;
    cout << "输入游戏者人数和报数间隔 : ";
    cin >> person_num >> cycle;
    for (int i = 1; i <= person_num; i++)
        clist.Insert(i - 1, i);            // 建立约瑟夫环
    Josephus(clist, person_num, cycle); // 解决约瑟夫问题

    return 0;
}

  程序的运行结果如下:

  

时间: 2024-10-12 22:52:50

用循环链表求解约瑟夫问题的相关文章

数据结构总结系列(三)——循环链表之约瑟夫问题

约瑟夫问题简介: 约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉.例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3,1. 分析: (1)由于对于每个人只有死和活两种状态,因此可以用布朗型数组标记每个人的状态,可用true表示死,false表示活. (2)开始时每个人都是活的,所以数组初值全部赋为false. (3)模拟杀人过程,直到所有人都被杀死为止. (由于博主懒癌犯了导致随便截取了某度百科的内容,或许更新之后会用这种方法呢~~

C++循环链表解决约瑟夫环问题

约瑟夫环问题可以简单的使用数组的方式实现,但是现在我使用循环链表的方法来实现,因为上午看到一道面试题规定使用循环链表解决约瑟夫环问题. 什么是约瑟夫环? “约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又出列:依此规律重复下去,直到圆桌周围的人全部出列.”(百度百科中的解决办法列出了很多,可以看到循环链表并不是最简单的方法) 这道面试题考察了循环链表的“创建”,

三种方法求解约瑟夫环问题

约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又出列:依此规律重复下去,直到圆桌周围的人全部出列. 方法1:使用stl::list模拟环形链表,参考剑指offer 代码: #include <iostream> #include <list> using namespace std; int lastNumber(unsigned int n,un

(java描述)关于链表的代码-----单双、循环链表、约瑟夫环、多项式相加

将链表头尾倒置 将几个链表合并成一个新的链表,将链表中重复的节点去掉,并按大小排序 双向循环链表 单向循环链表(约瑟夫循环) 多项式相加 程序源代码 单链表.单向循环链表结点类 package javab; public class Node { int data; Node next; public Node(int data){ this.data=data; } } 第一题代码: package javab; import java.util.Scanner; public class I

循环链表解决约瑟夫问题

训练一下尾插法和循环链表的使用. //循环链表解决约瑟夫问题 #include <stdio.h> #include <stdlib.h> typedef struct CycleLinkList { int data; struct CycleLinkList * next; }cycleLinkList; cycleLinkList * h, * r, * s; // 头指针.尾指针 int main() { int i, j; int n, num; cycleLinkLis

用循环链表解决约瑟夫问题

约瑟夫问题描述:从N个人中选出一个领导人,方法如下:所有人排除一个圆圈,按顺序数数,每数到第M的人出局,此时他两边的人 靠拢重新形成圆圈,从已出局人的下一个继续进行.问题是找出哪一个人将会是最后剩下的那个人,甚至我们更希望 知道出局人的顺序. 算法思路:构造一个循环链表来表示排成圆圈的人.每人的链接指向圆圈内他左边(或者右边)的人.圆圈内人第i个人用整数i 表示.首先为1号构造一个节点的循环链表,然后再把2~N号插入到1号节点之后,得到一个1~N的环,并时x指向节点N .然后从1号开始,跳过M-

C++ 用循环链表解决约瑟夫环问题

约瑟夫环问题 已知 n 个人(n>=1)围坐一圆桌周围,从 1 开始顺序编号,从序号为 1 的人开始报数,顺时针数到 m 的那个人出列.下一个人又从 1 开始报数,数到m 的那个人又出列.依此规则重复下去,直到所有人全部出列.请问最后一个出列的人的初始编号. 要求 输入人数 n,所报数 m,输出最后一个人的初始编号. 解决思路 首先因为是圆桌问题,使用链表解决的话需要构建循环链表. 接着是出列问题,这里我的设计思路是将指向链表的指针移动到需要出列的人的位置,然后根据正常的链表删除进行操作即可.

循环链表之约瑟夫问题

约瑟夫问题 (上课结束,大家听说第一周没有编程题目,立刻就被鄙视了,为了纠正这个错误,咱们本周就来做点简单题目.本题要求用循环链表实现) 约瑟夫问题是一个经典的问题.已知n个人(不妨分别以编号1,2,3,…,n 代表 )围坐在一张圆桌周围,从编号为 k 的人开始,从1开始顺时针报数1, 2, 3, ...,顺时针数到m 的那个人,出列并输出.然后从出列的下一个人开始,从1开始继续顺时针报数,数到m的那个人,出列并输出,…依此重复下去,直到圆桌周围的人全部出列. 输入:n, k, m 输出:按照出

javascript中使用循环链表实现约瑟夫环问题

1.问题 2.代码实现 /** * 使用循环链表实现解决约瑟夫环问题 * */ //链表节点 function Node(element){ this.element = element; this.next = null; } //定义链表类 function LList(){ this.head = new Node("head"); this.head.next = this.head; this.find = find; this.insert = insert; this.f