愚人節快樂!幾天沒寫程序,今天繼續!Josephus問題是說N個人圍成一個圈傳熱土豆,先約定一個數M,當傳遞了M次的時候拿着土豆的人出局,然後將土豆給出局人的下一個人,遊戲繼續,直到最後只剩下一個人,求出局人的序列(按出局順序排列)。
這個問題可以用數組實現,但是需要標記代表出局人的元素,並且沒遍歷一個元素就要檢查該元素是否已被標記爲出局,這樣程序運行時間必然會變慢。另一種方式是使用一個鏈表,每次把出局的節點刪除掉。這樣的解決方案非常直觀,只需要關注鏈表中的節點,因爲在鏈表中的節點就是沒有出局的。刪除節點只是指針的調整,非常迅速,但是釋放刪除的節點佔用的空間會比較費時,當輸入N很大時會佔用大量的時間。所以採用一種鏈表與數組結合的方式,可以克服兩種方式的缺點。
結合的方式就是把鏈表放在一個數組中!初始時,每個元素的next指針指向數組的下一個元素,最後一個指向第一個(我們需要的是一個循環鏈表)。這樣我們就可以實現在數組中跳躍性的訪問而不必關心訪問的節點是否是被標記爲出局,最後只要一次性釋放掉數組所佔用的空間就好了,不用管鏈表節點的釋放!
#include "stdafx.h"
#include <iostream>
using namespace std;
struct person
{
int id;
person* next;
};
struct person_list
{
int count;
person* first;
person* last;
};
person_list* init(int n)
{
person* p = new person[n];
for (int i = 0; i < n - 1; ++i)
{
p[i].id = i + 1;
p[i].next = &p[i+1];
}
p[n - 1].id = n;
p[n - 1].next = &p[0];
person_list* persons = new person_list;
persons->count = n;
persons->first = p;
persons->last = &p[n - 1];
return persons;
}
int* solve(int n,int m)
{
if(n < 1 || m < 0) return NULL;
person_list* persons = init(n);
person* first = persons->first;
person* t = first;
int count = persons->count;
int *losers = new int[n];
int j = 0;
while(count > 1)
{
person* q = NULL;
for (int i = 0; i < m; ++i)
{
q = first;
first = first->next;
}
if(first == persons->first)
{
persons->first = first->next;
persons->last = persons->first;
}
else if(first == persons->last)
{
q->next = first->next;
persons->last = q;
}
else
{
q->next = first->next;
}
--count;
losers[j++] = first->id;
first = first->next;
}
losers[j] = persons->first->id;
delete []t;
delete persons;
return losers;
}
int _tmain(int argc, _TCHAR* argv[])
{
int n = 5, m = 1;
int* losers = solve(n,m);
for (int i = 0; i < n; ++i)
{
cout<<losers[i]<<' ';
}
cout<<endl;
delete[]losers;
return 0;
}
程序中losers數組的最後一個元素保存的是最後勝利的人,之前纔是出局人序列。
生活中許多事情也像寫程序,哪裏不會出點bug呢,但是bug總會被修復的不是嗎?