1. 程式人生 > 其它 >【資料結構】約瑟夫問題(連結串列法)

【資料結構】約瑟夫問題(連結串列法)

什麼是約瑟夫問題?

約瑟夫問題:n個人圍成一圈,初始編號從1~n排列,從約定編號為x的人開始報數,數到第m個人出圈,接著又從1開始報數,報到第m個數的人又退出圈,以此類推,最後圈內只剩下一個人,這個人就是贏家,求出贏家的編號。

是不是有點點複雜,其實該問題歸結為模擬型別的演算法題,根據題目要求模擬即可。

環形連結串列


1、先建立一個環形連結串列來存放元素:

2、然後一邊遍歷連結串列一遍刪除,直到連結串列只剩下一個節點,我這裡就不全部演示了

那麼,通過前面對環形連結串列的瞭解,以你的智慧應該會輕鬆解決簡單的約瑟夫問題了。

不妨我們加大難度,設有編號為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\n",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("\n倖存者:%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;
}