问题 A: 约瑟夫问题(普及第一关模拟)
題目描述
求解約瑟夫(Joseph)問題。有n個小孩圍成一圈,給他們從1開始依次編號,從編號為1的小孩開始報數,數到第m個小孩出列,然后從出列的下一個小孩重新開始報數,數到第m個小孩又出列,…,如此反復直到所有的小孩全部出列為止,求整個出列序列。
如當n=6,m=5時的出列序列是5,4,6,2,3,1。n,m不大于20
輸入
n m的值
輸出
出列序列
樣例輸入
6 5樣例輸出
5 4 6 2 3 1問題解析
我們可以看到這個約瑟夫問題(即小孩報數)的問題,其實就是鏈表問題。我們通過這道題需要掌握的是循環單鏈表的寫法(以c語言為例)
一、循環鏈表的寫法
1.先定義鏈表的結構體
struct child{int data;struct child* next; };然后再全局變量定?“頭節點”和“約瑟夫環的兩個參數”
struct child* headnode; int n,m;下面,我們來定義兩個函數,分別是“創建空節點”與“創建有數據的節點”
struct child* emptynode(){struct child* p = (struct child*)malloc(sizeof(struct child));p->data = NULL;p->next = NULL;return p; }struct child* createnode(int num){struct child* p = (struct child*)malloc(sizeof(struct child));p->data = num;p->next = NULL;return p; }我們可以看出這兩段代碼的主要差別,其實就是p->data=NULL和p->data=num
接下來定義一個函數“createlistBytail”,以尾接法,創建循環單鏈表
void createlistBytail(struct child* headnode,int num){struct child* r=headnode;for (int i=2; i<=num; i++ ){struct child* s = createnode(i);s->next = r->next;r->next = s;r=s;} r->next = headnode; }那么其中需要注意的是,這段代碼看似為單純的創建單鏈表,其實我們在結尾處添加了一段代碼
r->next = headnode;這段代碼保證了單鏈表的循環,也正因為這段代碼,使得我們的單鏈表成功升級為循環單鏈表!
當然,為了檢驗我們的鏈表是否創建成功,我們可以寫入這列代碼,來遍歷出鏈表的數據
void printlist(struct child* headnode){struct child* pMove = headnode;while(pMove) {printf("%d",pMove->data );pMove = pMove->next ;} }?接下來,我們開始創建solve函數,實現小孩的報數功能
void solve(struct child* headnode,int n,int m){int i,j;struct child* p;struct child* q;for(i=1;i<=n;i++){p = headnode;j = 1;for(j=1;j<m-1;j++){p = p->next ;}q = p->next ;printf("%d ",q->data );p->next = q->next ;headnode = p->next ;} }其中,我們需要注意的是?我們在進行第二個for循環時,j<m-1這個數值不要搞錯。我們為什么要j<m-1呢?因為我們要保證p是在第m個人的前一個人,這樣我們就可以使q指向p->next,也就是我們想要的那個第m個人,實現精準查找,然后精準“刪除”。但是問題來了,我們為什么不直接將p指向第m個人呢,這樣我們直接輸出p不就好了么:(。非也非也!你只是想到了輸出數值的事,但是后面的事你卻沒能想到。
我們在這段函數的主要任務是什么呢?1.輸出第m個人2.刪除m,讓m前一個人接上m后一個人3.將領頭人headnode變成m的下一個人,也就是說從m的下一個人開始報數。這樣我們不難發現我們將p設為m的前一個人,是要將p-next指向q->next,實現刪除m的目的,因為我們節點的指針能指向某節點的后面,卻指向不了節點的前面,所以哦我們需要這樣的一個p指向m前一個點。
最后,我們編寫主函數,將上面的函數包裝進去
其中我們要注意的是,創建空節點與數據節點不在主函數里。
int main(){struct child* headnode = createnode(1);scanf("%d %d",&n,&m);createlistBytail(headnode,n);//printlist(headnode);solve(headnode,n,m);return 0; }這樣,我們的代碼就算結束了。最后,給大家奉上“小孩報數”(即約瑟夫問題)的完整版代碼
#include<stdio.h> #include<stdlib.h> struct child{int data;struct child* next; };struct child* headnode; int n,m;struct child* emptynode(){struct child* p = (struct child*)malloc(sizeof(struct child));p->data = NULL;p->next = NULL;return p; }struct child* createnode(int num){struct child* p = (struct child*)malloc(sizeof(struct child));p->data = num;p->next = NULL;return p; }void createlistBytail(struct child* headnode,int num){struct child* r=headnode;for (int i=2; i<=num; i++ ){struct child* s = createnode(i);s->next = r->next;r->next = s;r=s;} r->next = headnode; }void printlist(struct child* headnode){struct child* pMove = headnode;while(pMove) {printf("%d",pMove->data );pMove = pMove->next ;} }void solve(struct child* headnode,int n,int m){int i,j;struct child* p;struct child* q;for(i=1;i<=n;i++){p = headnode;j = 1;for(j=1;j<m-1;j++){p = p->next ;}q = p->next ;printf("%d ",q->data );p->next = q->next ;headnode = p->next ;} }int main(){struct child* headnode = createnode(1);scanf("%d %d",&n,&m);createlistBytail(headnode,n);//printlist(headnode);solve(headnode,n,m);return 0; }?
?
總結
以上是生活随笔為你收集整理的问题 A: 约瑟夫问题(普及第一关模拟)的全部內容,希望文章能夠幫你解決所遇到的問題。