1. 程式人生 > >資料結構實戰——Joseph 環的實現

資料結構實戰——Joseph 環的實現

問題描述

    約瑟夫環問題(Joseph)又稱丟手絹問題:已知 m 個人圍坐成一圈,由某人起頭,下一個人開始從 1 遞增報數,報到數字 n 的那個人出列,他的下一個人又從 1 開始報數,數到 n 的那個人又出列;依此規律重複下去,直到 m 個人全部出列約瑟夫環結束。如果從 0 ~ (m-1) 給這 m 個人編號,請輸出這 m 個人的出列順序。

解決思路

    本題的解決思路有兩種:一是用迴圈連結串列解決,這會在後續的部落格中給出解決方法;二是用線性表陣列表示法解決。

    線性表陣列解決方式有 2 種:1 是利用上篇部落格提到的 ListDelete 函式,凡是數到 n 的那個出列元素直接刪去;2 是設定標記,凡是數到 n 的那個出列元素,置為 -1 ,下一輪開始值為 -1 的不參與報數。

    如何用陣列表示迴圈呢,即現在的問題是,數到 m-1 ,如何再尋回至0 ?這裡可以採用 取模運算子 % ,即  i = (i+1) % m ,其中 i = [ 0 , m-1] 。


詳細程式碼

/* 實現Joseph Ring */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "SlistOperation.h"

void InitJoseph(SLIST &L, int num){
    for(int i=1;i<=num;i++){
        ListInsert(L,i,i-1);
    }
}

void Joseph_1(SLIST L, int jump){
    int st = 0, pos = 1, item, temp;
    while(!ListEmpty(L)){
        while( st != jump ){
            GetElem(L,pos,temp);    //獲取當前pos位序的元素temp
            if (pos < ListLength(L)) //獲取temp的後繼item
                NextElem(L,temp,item);
            else
                item = L.A[0];
            pos = LocateElem(L,item);   //獲取後繼item的位序
            st++;   //用於判斷出圈元素
        }
        ListDelete(L,pos,temp); //滿足條件的pos即刪除並列印
        printf("%d ",temp);
        st = 1;
        if (pos > ListLength(L))
            pos = 1;
    }
    printf("\n");
}

void Joseph_2(SLIST L, int num, int jump){
    int i = 0, pos = 1;
    for (int j = 0; j < num; j++){	//規定輸出次數為m
        while (i != jump){
            if (pos == ListLength(L))	//獲取下一位
                pos = 1;
            else
                pos++;
            if (L.A[pos-1] >= 0)	//當前位置為-1,說明已出列
                i++;
        }
        i = 0;
        printf("%d ",L.A[pos-1]);
        L.A[pos-1] = -1;	//出列位記為-1
    }
}

int main(){
    int m,n;
    SLIST L;
    InitList(L);
    printf("請輸入環的總數: ");
    scanf("%d",&m);
    printf("請輸入跳數:");
    scanf("%d",&n);
    if (m<=0 || n<=0){
        printf("Input Error\n");
        system("pause");
        return 0;
    }

    InitJoseph(L,m);
    printf("\n變換前的初始環為:");
    ListTravers(L);
    printf("\n約瑟夫環解法一為:");  //ListDelete()刪除出隊元素
    Joseph_1(L,n);
    printf("\n約瑟夫環解法二為:");  //出隊元素置為-1
    Joseph_2(L,m,n);
    printf("\n");
    return 0;
}

程式執行的結果示例,截圖如下:


數學求解

    數學求解的思路是將約瑟夫問題歸納為數學問題,即推匯出遞推公式。好處是合理採用數學策略,使得程式設計簡單,時間複雜度為 O(n),但是隻能得到最後出列的那個人的序號。     對於 m 個人,編號為 0~(m-1) ,其中第 k 個人出列,即 k-1 出列。 -> 0, 1,... ... ,k-1,k,k+1,... ...,m-1 (1)     出列元素為 (n-1) % m。 -> 0, 1,... ... ,k-2,k,k+1,... ...,m-1     延長此序列為: -> 0, 1,... ... ,k-2,k,k+1,... ...,m-1,m,m+1,... ...,m+k-2     出去 0~k-2 元素 -> k,k+1,... ...,m-1,m,m+1,... ...,m+k-2     減去k -> 0, 1,... ... ,m-2(2)     上述過程演示的就是 m 個人到 m-1 個人的變化,即可以通過遞推得到約瑟夫環最後一個輸出元素。現在從(2)回推 (1) ,假設(2)中只剩下最後一個元素 x,如果回推到(1)中對應的位序是x`,那麼問題就得到了解決。     顯然我們可以得到 x` = (x+k) % m,於是就可以得到遞推公式:f[1] = 0;f[i] = (f[i-1] + n) % i ,(i > 1)。 微笑
 好了,這個問題就說到這兒。有問題請聯絡我:[email protected] 轉載請宣告出處:http://bolg.csdn.net/zhongkelee  原始碼下載

相關推薦

資料結構實戰——Joseph 實現

問題描述     約瑟夫環問題(Joseph)又稱丟手絹問題:已知 m 個人圍坐成一圈,由某人起頭,下一個人開始從 1 遞增報數,報到數字 n 的那個人出列,他的下一個人又從 1 開始報數,數到 n 的那個人又出列;依此規律重複下去,直到 m 個人全部出列約瑟夫環結束。如

資料結構基礎及STL實現(複習)

目錄 動態陣列 棧 佇列 優先佇列 動態陣列 srand(time(0)); std::vector<int> qwq; for(int i = 1;i <= 10;++i) qwq.push_back(rand(

資料結構實戰完全手冊-夏曹俊-專題視訊課程

資料結構實戰完全手冊—85人已學習 課程介紹         資料結構是程式設計的必修知識,它是程式設計的基本功,並且在企業面試、日常工作、研究生入學考試中都佔有重要的地位。不同於其他課程,本課程從

資料結構——棧操作的實現

#include <stdio.h> #include <malloc.h> #define OK 1 #define ERROR 0 #define INFEASIBLE -1 #define OVERFLOW -2 #define TRUE 1 #define FALSE

c_資料結構_隊的實現

# 鏈式儲存#include<stdio.h> #include<stdlib.h> #define STACK_INIT_SIZE 100//儲存空間初始分配量 #define STACKINCREMENT 10//儲存空間分配增量 #define TURE 1 #defi

資料結構】堆的實現

文章目錄 Heap.h Heap.c Test.c Heap.h #ifndef __HEAP_H__ #define __HEAP_H__ #include<assert.h> #include

資料結構)HashTable的實現

#include<iostream> #include<iomanip> #include<vector> #include<list> using namespace std; template<class Iterator,clas

資料結構-佇列-連結串列實現

程式碼實現 //佇列 連結串列實現 #include <stdio.h> #include <stdlib.h> #include <stdbool.h> //定義一個連結串列 typedef struct _Node Node; struct

資料結構-佇列-順序表實現-C語言

佇列定義 對於一個存取的n個內容,最先進入的最先出去(First In,First Out:FIFO),即稱為佇列. 比如,食堂排隊,最先去的,最先得到飯菜; 關鍵步驟:入隊出隊 程式碼實現 //迴圈佇列 順序表實現 #include <stdio

資料結構-堆疊-連結串列實現

/* * 堆疊 連結串列實現 * * */ #include <stdio.h> #include <stdlib.h> #include <stdbool.h> //定義結構 typedef struct _Stack * Stack; s

資料結構之連結串列實現佇列

#include<stdio.h>#include<malloc.h>#include<stdlib.h>#define OK 1#define ERROR 0typedef struct Node{ int data; struct Node *next;}Node,*L

資料結構&堆&heap&priority_queue&實現

目錄 什麼是堆? 大根堆 小根堆 堆的操作 什麼是堆? 堆是一種資料結構,可以用來實現優先佇列 大根堆 大根堆,顧名思義就是根節點最大。我們先用小根堆的建堆過程學習堆的思想。 小根堆 下圖為小根堆建堆過程 堆的操作 上浮 下沉 插入 彈出 取頂 堆排序 STL heap 所在庫 #include

資料結構與演算法-java實現】二 複雜度分析(下):最好、最壞、平均、均攤時間複雜度的概念

上一篇文章學習了:如何分析、統計演算法的執行效率和資源消耗? 點選連結檢視上一篇文章:複雜度分析上 今天的文章學習以下內容: 最好情況時間複雜度 最壞情況時間複雜度 平均情況時間複雜度 均攤時間複雜度 1、最好與最壞情況時間複雜度 我們首先

資料結構——使用Java棧實現【括號匹配】

給定一個只包括 '(',')','{','}','[',']'的字串,判斷字串是否有效。 有效字串需滿足: 左括號必須用相同型別的右括號閉合。 左括號必須以正確的順序閉合。 注意空字串可被認為是有效字串。 參考leetcode.com或leetcode-cn.com

資料結構實戰01

題目: 這是一個約瑟夫環問題,用C語言陣列解決如下 #define MAXN 100 void printring(int a[],int n,int i,int k){ int b[MAXN],j,s; for(j=0;j<n;j++){ b[

資料結構C/C++程式碼實現 棧連結串列基本操作

實現棧連結串列基本操作: #include<stdio.h> #include<stdlib.h> typedef int ElemType; typedef struct linknode {     ElemType data;     stru

資料結構C/C++程式碼實現 順序表棧基本操作

順序表棧基本操作的實現  原始碼: #include<stdio.h> #include<stdlib.h> #include<iostream> using namespace std; #define MAXSIZE 100 //#d

資料結構學習筆記——C++實現雙向迴圈連結串列模板類(超詳解)

定義了兩個標頭檔案分別放置結點類模板(Node.h)和雙鏈表模板(DoubleLinkList.h), 然後在原始檔的main函式中測試。 Node.h #pragma once # include <iostream> template <class

資料結構】順序表實現十進位制轉換任意進位制

/* & File : 進位制準換 * Author : Laugh * Copyright: Laugh * 主題 :對於輸入的任意一個非負十進位制小數,列印輸出與其等值的任意進位制小數 * Date : 2018/10/14 */

資料結構(C語言實現):判斷兩棵二叉樹是否相等,bug求解

判斷兩棵二叉樹是否相等。 遇到了bug,求大神幫忙!!! C語言原始碼: #include <stdio.h> #include <stdlib.h> #include <malloc.h> #define OK 1 #define