日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

约瑟夫问题的学习(基于循环链表)以及基于循环数组

發布時間:2024/4/13 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 约瑟夫问题的学习(基于循环链表)以及基于循环数组 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這是17世紀法國數學家加斯帕在《數目中的游戲問題》講的一個問題:15個教徒和15個非教徒在海上遇險,必須將一般的人投入海中,其他的人才能幸免于難。與實現各一個辦法:30個人圍成一個圈,從第個人開始報數,每報到第9個人就將他扔入海中,如此循環進行,直至僅余15個人為止。問怎么樣排法,才能使每次投入大海的都是非教徒。
可參照以下代碼:
#include<stdio.h>
typedef struct node_s
{
int next;//指向下一個人的指針(下一個人的數組下標)
int no_out;//是否被扔下海的標志,1:沒有被扔下海2:被扔下海。
}node_t;
node_t link[31];//30個人,0號元素沒有使用。
int main()
{
int i,j,k;
printf(“The original cir is (+:pagandom,@:christian):\n”);
for(i=1;i<=30;i++)//初始化數組
{
link[i].next=i+1;//指針指向下一個人(下一個數組下標)
link[i].no_out=1;//標志置為1,表示都在船上。
}
link[30].next=1;//第30個人的指針指向第一個人以構成環。
j=30;//j指向已經處理完畢的數組元素,從link[j]指向的人開始計數
for(i=0;i<15;i++)//已扔下海的人數計數器
{
for(k=0;?//決定哪個人被扔下海的計數器
if(k<15)//計數不到15則繼續計數
{
j=link[j].next;//修改指針,指向下一個人
k+=link[j].no_out;//進行計數,因已扔下海的人標志為0
}//這樣標志不會影響計數。
else
break;//計數到15就停止計數
link[j].no_out=0;//標志置0,表示該人扔下海
}
for(i=1;i<=30;i++)//輸出結果。
{
printf("%d:%c\n",i,link[i].no_out?’+’:’@’);
}//’+’:表示被扔下海的非教徒,‘@’:表示留在船上的教徒
return 0;
}


#if 0
下面是轉自別人的博客
約瑟夫環問題,這是一個很經典算法,處理的關鍵是:偽鏈表

問題描述:N個人圍成一圈,從第一個人開始報數,報到m的人出圈,剩下的人繼續從1開始報數,報到m的人出圈;如此往復,直到所有人出圈。(模擬此過程,輸出出圈的人的序號)

在數據結構與算法書上,這個是用鏈表解決的。我感覺鏈表使用起來很麻煩,并且這個用鏈表處理起來也不是最佳的。

我畫了一個圖用來理解:

有如下問題需要首先考慮:

1、“圈”怎樣形成?

以上圖為例:下標從0開始,當下標為11的時候,在加1,就應該回到0。index = (index+1) % count;

2、怎樣處理?數組or鏈表or其他方法?

鏈表使用起來很笨重,我們有循環數組的方法。

解法一程序分析:

循環的開始和結束:循環的結束取決于圈內是否還有“人”,可以用一個變量alive表示初始人數,每一次出圈,alive-1.判斷alive是否非0即可。

while(alive > 0)

每一次循環,就是“過”一個人,但是,這個人有兩種不同的狀態:在圈內和不在圈內;在圈內就報數,number+1,不在圈內就不參與報數,number不變。

假設有N個int元素的數組,每一個int元素表示一個“人”;并且,取值為0和1, 1表示在圈內,0表示不在圈內,所以,如果這個人在圈內,number+1;如果這個人不在圈內,number+0。那么,在報數的時候,不需要考慮這個人在不在圈內就行(每一個人都需要加1或加0,所以,可以在這塊優化一下程序)。

void joseph(int count, int doom) {
int alive = count; //幸存人數
int number = 0; //計數,當number==doom時,淘汰這個人
int index = 0; //下標,為總人數-1
int *circle = NULL; //根據需求設為循環數組,存儲每個人

//用calloc()函數申請得到的空間,自動初始化每個元素為0 //所以,0表示在這個人在約瑟夫環內,1表示這個人出圈,即“淘汰” circle = (int *) calloc(sizeof(int), count);//只要幸存人數大于0,則一直進行循環 while(alive > 0) {number += 1- circle[index]; //每輪到一個人報數,不管是"0"還是"1"都進行計數 if(number == doom) { //當number==doom時,就要淘汰當前這個人/*淘汰一個人需要做四步操作:1、輸出這個人的位置 2、把這個人的狀態從在圈內"0"改為不在圈內"1" 3、幸存人數alive-- 4、 計數器number歸零 */ alive == 1 ? printf("%d", index+1) : printf("%d,", index+1);circle[index] = 1;alive--;number = 0;}//與總人數count取余,則可以使index在0~count-1之間 一直循環,達到循環數組的目的 index = (index +1) % count; } printf("\n");free(circle); //結束后一定要釋放circle所申請的空間

}

解法二程序分析:

解法二在解法一的基礎上進行了優化,對出圈的人的節點進行刪除,可以減少時間復雜度。

假設,要刪除的節點下標為curIndex,其前驅節點下標為preIndex;

circle[preIndex] = circle[curIndex];假設要刪除的節點下標curIndex=3,那么circle[2] = circle[3] ,circle[2] = 4,circle[2]之前等于3,現在等于4。即下標為3的第四個人已經被刪除了。

怎樣遍歷?

preIndex = curIndex;curIndex = circle[curIndex];

但是,在出圈的時候,curIndex 和preIndex 的變化有別于上面的操作:

出圈時,curIndex 需要后移,preIndex 應該不動!

每次循環,直接報數,因為被刪除的人(例如上面的第四個人)不可能進行報數了。

void joseph(int count, int doom) {
int alive = count; // 幸存人數
int number = 0; // 報數的數
int curIndex = 0; // 當前人下標
int preIndex = count - 1; // 前一個人下標
int *circle = NULL;
int index;

circle = (int *) malloc (sizeof(int) * count); //對circle數組進行初始化 for(index = 0; index < count; index++) {circle[index] = (index + 1) % count; }while(alive > 0) {number++;if(number == doom) {alive == 1 ? printf("%d", curIndex+1) : printf("%d,", curIndex+1); alive--;number = 0;circle[preIndex] = circle[curIndex]; //出圈操作 } else {preIndex = curIndex; //處理下一個人 }curIndex = circle[curIndex]; }free(circle);

}

解法三程序分析:

解法三里沒有進行number報數,而是直接計算出需要移動的人數,然后定位到要出圈的人。

num = doom % alive - 1;

for(index = 0; index < (num == -1 ? alive - 1 : num); index++) {
preIndex = curIndex;
curIndex = circle[curIndex];
}

void joseph(int count, int doom) {
int alive = count; // 幸存人數
int curIndex = 0; // 當前人下標
int preIndex = count - 1; // 前一個人下標
int *circle = NULL;
int index;

circle = (int *) malloc(sizeof(int) * count); for(index = 0; index < count; index++) {circle[index] = (index + 1) % count; // 初始化鏈表 }while(alive > 0) { // 只要還有幸存者,就繼續“殺”int num = doom % alive - 1; // 直接計算出需要移動的人數,// 直接定位到要出圈的人for(index = 0; index < (num == -1 ? alive - 1 : num); index++) {preIndex = curIndex;curIndex = circle[curIndex];}// 該人出圈!alive == 1 ? printf("%d", curIndex+1) : printf("%d,", curIndex+1); alive--;circle[preIndex] = circle[curIndex]; // 真正的出圈操作!curIndex = circle[curIndex]; // 繼續處理下一個人 } // 這個算法比normalJoseph.c效率提高30%!free(circle); }

#endif

總結

以上是生活随笔為你收集整理的约瑟夫问题的学习(基于循环链表)以及基于循环数组的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。