• 【数据结构】约瑟夫问题(链表法)


    什么是约瑟夫问题?

    约瑟夫问题:n个人围成一圈,初始编号从1~n排列,从约定编号为x的人开始报数,数到第m个人出圈,接着又从1开始报数,报到第m个数的人又退出圈,以此类推,最后圈内只剩下一个人,这个人就是赢家,求出赢家的编号。

    是不是有点点复杂,其实该问题归结为模拟类型的算法题,根据题目要求模拟即可。

    环形链表

    image
    1、先创建一个环形链表来存放元素:

    2、然后一边遍历链表一遍删除,直到链表只剩下一个节点,我这里就不全部演示了
    image

    那么,通过前面对环形链表的了解,以你的智慧应该会轻松解决简单的约瑟夫问题了。

    不妨我们加大难度,设有编号为1,2,3,...,n(n>0)个人按顺时针顺序围坐一圈,没人手持一个随机产生的密码(正整数)。现从第k个人开始顺时针的方向以1开始报数,报数的上限第一个人持有的密码m,报到m的人出列,然后将出列的人所持有的密码作为新的m值,从下一个人开始重新从1开始报数,如此下去,直到最后一个人出列为止,要求设计一个程序模拟此过程,并输出他们的列编号序列

    问题分析

    很显然这是一个线性结构,可以用线性表来表示。进行的主要操作是报数、出列,这个相当于对线性表进行删除操作,因此宜用用链表存储结构,每个节点表示一个人。n个人围城一圈循环报数,则利用单循环链表存储结构更容易解决问题。
    因此,
    第一步需要创建单循环链表,每个节点存储节点序号和密码,密码随机生成(1-10)
    第二步实现循环删除操作,将密码作为每次循环的步长(这里指的是距离)
    第三步实现打印操作(跟随第二步操作)

    程序设计

    /*单循环链表的存储结构*/
    typedef struct
    {
        int num;//序号
        int pwd;//密码
        struct Joseph *next;
    
    }Joseph;
    

    输入设计

    在一行中输入总人数和第几个人开始报数

    输入设计

    首先输出每个人的编号以及持有的密码,其次输出出列的编号次序,最后输出幸存者。

    基本操作

    创建链表Joseph* creatList(Joseph* head,int n);
    输出每个人的序号以及密码:void printList(Joseph* head,int n);
    初始从第k个人开始报数,输出出列次序:void outList(Joseph *head,int k);

    程序代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <malloc.h>
    /*约瑟夫环问题
    *单循环链表的创建
    *打印单循环链表
    *循环淘汰m,最后留下幸存者
    */
    typedef struct
    {
        int num;
        int pwd;
        struct Joseph *next;
    
    }Joseph;
    Joseph* creatList(Joseph* head,int n)
    {
        int i;
        Joseph *p,*q;
        q = head;
        q->num = 1;
        q->pwd = rand()%10+1;
    
    
        for(i = 2;i<=n;i++)              //尾插法
        {
            p = (Joseph*)malloc(sizeof(Joseph));
            p->num = i;
            p->pwd = rand()%10+1;
            q->next = p;
            q = p;
    
        }
        p->next = head;
    
    }
    void printList(Joseph* head,int n)
    {
        int i;
        Joseph *list;
        list = head;
        for(i = 0;i<n;i++)
        {
            printf("第%d位的密码是%d
    ",list->num,list->pwd);
            list = list->next;
        }
    
    }
    void outList(Joseph *head,int k)
    {
        Joseph *p,*q;
        q = (Joseph*)malloc(sizeof(Joseph));
        p = head;
        int m;
    
    
        for(int i = 1;i<k;i++)              //找第k个节点,作为开始
        {
            q = p;
            p = p->next;
        }
    
        m = head->pwd;                       //第k个节点的密码作为步长(这里指的是距离)
        while(p!=q)                          //当留下最后一个节点时p = q
        {
            for(int i = 1;i<m;i++)
            {
                q = p;
                p = p->next;
            }
            m = p->pwd;
            printf("%d ",p->num);
            q->next = p->next;
            free(p);
            p = q->next;
        }
        printf("
    幸存者:%d",q->num);
        free(p);
    
    
    }
    
    
    
    int main()
    {
        Joseph *joseph;
        joseph = (Joseph*)malloc(sizeof(Joseph));
        int n;
        printf("输入总人数:");
        scanf("%d",&n);
    
        creatList(joseph,n);
        printList(joseph,n);
    
        int k;
        printf("输入从第几个人开始报数:");
        scanf("%d",&k);
    
        outList(joseph,k);
    
    
        return 0;
    }
    
    

    约瑟夫问题:一行代码就解决了 ————转载

  • 相关阅读:
    Android ContentProvider和getContentResolver
    onContextItemSelected 用法
    Android 控件 之 Menu 菜单
    Android
    Android Cursor类的概念和用法
    android SQLiteOpenHelper使用示例
    JAVA HashMap详细介绍和示例
    HashMap深度解析(二)
    HashMap深度解析(一)
    使用svn遇到的问题---(在编辑器没有配置svn的前提下)
  • 原文地址:https://www.cnblogs.com/zhujiaozhu/p/15388058.html
Copyright © 2020-2023  润新知