数据结构课的一道作业题如下:
设有n个人围坐在圆桌周围(圆桌会议?),现从第s个人开始报数,数到第m的人出列。(哈,不就是约瑟夫问题吗,这人一定死了!)然后从出列的下一个人开始重新报数,数到m的人又出列,以此类推…按照出列人的顺序,输出他们的序号。
所以约瑟夫站在哪个位置才能不死呢?这是后话。先来看看约瑟夫问题的由来。
这是以夫拉维·约瑟夫(他是公元一世纪时著名历史学家,如果不是他的数学天赋,他是不会活到那一天的)命名的问题。在犹太罗马战争期间,他们41名犹太反抗者困在了罗马人包围的洞穴中。这些反抗者宁愿自杀也不愿被活捉,于是决定围成一个圆圈,并沿着圆圈每隔两个人杀死一个人,直到剩下两个人为止。但是,约瑟夫和一个未被告发的同谋者不希望无谓的自杀,于是他迅速计算出他和其朋友在这个险恶的圆圈中应该站的位置。
约瑟夫的情况可谓是到了危急存亡的关头,如何迅速算出“生还位置”才是关键。
下面,先对于上面那道作业题给出相应的几种解法。
不进行删除操作的数组解法:
#include <iostream>
#include <vector>
#include <ostream>
using namespace std;
vector<int> JosephusArray(int n, int m, int s) {
vector<int> ans;
int* p = new int[n];
memset(p,0,n);
int countM = 0;
int countN = n;
for(int i = s-1;countN != 0;i = (i+1) % n) {
if(p[i] == -1) {
continue;
}
countM++;
if(countM == m) {
countM = 0;
p[i] = -1; //出列的位置标记为-1
ans.push_back(i+1);
countN--;
}
}
return ans;
} // 数组标记方法,不删除数组元素
ostream & operator << (ostream & o, const vector<int> & arr) {
for(vector<int>::const_iterator i = arr.begin(); i != arr.end(); ++i) {
o << *i << " ";
}
return o;
}
int main() {
cout << JosephusArray(4, 2, 1);
//(n, m, s)
return 0;
}
数组二:由于上面方法在最后数组的-1元素很多,必须一个一个判断,丧失了数组随机访问速度,所以如果删除掉出列元素,可省去与-1比较的时间。
#include <iostream>
#include <vector>
#include <ostream>
using namespace std;
class Array {
private:
int* p;
int size;
public:
Array(int n):p(new int[n]), size(n) {
for(int i = 0; i < n; ++i)
p[i] = i+1;
}
~Array() {
if(p != NULL) {
delete [] p;
size = 0;
}
}
bool deleteElement(const int & i);
friend vector<int> JosephusArray(int n, int m, int s);
};
bool Array::deleteElement(const int & i) { //i为下标,即第i+1个元素
if(size < i || i < 0) {
cout << "下标越界" << endl;
return false;
}
if(i == size-1) {
size--;
return true;
}
for(int j = i; j < size-1; ++j)
p[j] = p[j+1];
size--;
return true;
}
vector<int> JosephusArray(int n, int m, int s) {
vector<int> ans;
Array array(n);
for(int i = (s-1+m-1) % array.size; array.size != 0; i = (i+m-1) % array.size) {
ans.push_back(array.p[i]);
array.deleteElement(i);
}
return ans;
}
ostream & operator << (ostream & o, const vector<int> & arr) {
for(vector<int>::const_iterator i = arr.begin(); i != arr.end(); ++i) {
o << *i << " ";
}
return o;
}
int main() {
cout << JosephusArray(4, 2, 1);
return 0;
}
有一个小问题:这个代码报浮点异常的错误,网上说可能是a%0的缘故或者gcc链接库版本不匹配。但是,检查了一下,应该没有逻辑问题,array.size==0时,就跳出了。有点玄乎>_<
链表解法:优势:删除的操作更少。劣势,不能随机访问,必须遍历。
这里要自行实现链表的数据结构和成员函数。
Link是节点类
LnkLink是链表
其中,makeCircular()是把单链表建成循环链表的函数。这样,不用取模,自己成环,环环相扣,题目可解。
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
template <class T>
class Link {
public:
T data;
Link<T> *next;
Link(const T info = 0) {
data = info;
next = NULL;
}
~Link(){next = NULL;}
};
template <class T>
class LnkList {
private:
Link<T> *head, *tail;
int size;
public:
LnkList();
~LnkList();
bool isEmpty();
void clear();
//int length();
bool append(const T value);
Link<T>* getPosPtr(const int p);
Link<T>* getPosPtr(Link<T>* start, const int p);
void makeCircular() {
tail->next = head -> next;
}
bool delNode(Link<T> *l);
void show();
};
template <class T>
LnkList<T>::LnkList() {
head = tail = new Link<T>; //头结点
size = 0;
}
template <class T>
LnkList<T>::~LnkList() {
if(size == 0) {
delete head;
}
else {
Link<T>* p = head->next;
while(p != tail) {
Link<T>* tmp = p;
p = p -> next;
delete tmp;
}
delete tail;
delete head;
}
}
template <class T>
bool LnkList<T>::append(const T value) {
tail->next = new Link<T>(value);
tail = tail -> next;
++size;
return true;
}
template <class T>
bool LnkList<T>::isEmpty() {
return size == 0 ? true: false;
}
template <class T>
void LnkList<T>::clear() {
Link<T>* p = head->next;
while(p != tail) {
Link<T>* tmp = p;
p = p->next;
delete tmp;
}
delete tail;
head->next = NULL;
size = 0;
}
template <class T>
Link<T>* LnkList<T>::getPosPtr(const int p) {
Link<T>* h = head;
for(int i = 0;i < p; ++i) {
h = h->next;
}
return h;
}
template <class T>
Link<T>* LnkList<T>::getPosPtr(Link<T>* start, const int p) {
Link<T>* s = start;
for(int i = 0;i < p; ++i) {
s = s -> next;
}
return s;
}
template <class T>
bool LnkList<T>::delNode(Link<T> *l) {
Link<T>* t = head->next;
while(t != tail && t->next != l) {
t = t -> next;
}
t->next = l -> next;
if(l == head -> next) {
head -> next = l -> next;
}
delete l;
--size;
return true;
}
template <class T>
void LnkList<T>::show() {
Link<T>* p = head->next;
cout << p->data << " ";
p = p->next;
while(p != head->next) {
cout << p->data << " ";
p = p ->next;
}
}
vector<int> JosephusLink(int n, int m, int s) {
LnkList<int> ll;
vector<int> res;
for(int i = 0; i < n; ++i) {
ll.append(i+1);
}
ll.makeCircular();
Link<int>* p = ll.getPosPtr(s);
//cout << p -> data;
//ll.show();
int i = 0;
while( !ll.isEmpty()) {
i++;
Link<int>* tmp = ll.getPosPtr(p,m-1);
//cout << tmp -> data <<" " << i << endl;
p = tmp -> next;
res.push_back(tmp->data);
ll.delNode(tmp);
}
return res;
}
ostream & operator << (ostream & o, const vector<int> & v) {
for(vector<int>::const_iterator it = v.begin(); it != v.end(); ++it)
o << *it << " ";
o << endl;
return o;
}
int main() {
cout << JosephusLink(4,5,1);
return 0;
}