约瑟夫环的问题大家不陌生了吧,在n个人中循环喊口号1~m,喊到m的退出,求退出次序。
首先,循环链表的类可以这么实现(已略去与本题不相关的成员函数):
如需查看完整循环链表类实现,请参考我的GitHub:循环链表完整类
//所有下标均从1开始计数
template<class T>
class CircleList {
protected:
int n;
node<T>* head;
node<T>* tail;
node<T>* ptr; //这里为什么需要一个ptr指针呢? 因为删除操作有了这个更好操作,删除到哪,ptr就指到哪。
public:
CircleList():n(0) {init();ptr=head;} //构造函数, 结点数初始化为0
void init() { //初始化链表
head = new node<T>;
head->next = head;
head->last = head;
tail = head;
}
void Delete(int pos) { //删除结点
while(pos--) {
baby=baby->next;
if(baby == head) pos++; //头结点需要特殊处理一下,不算在pos位移之内
}
ptr->last->next = ptr->next;
ptr->next->last = ptr->last;
node<T>* tmp = ptr->last; //临时储存ptr的上一个节点,以便下次使用
delete ptr;
ptr=tmp;
n--;
}
T At(int pos) { //查询pos位置上的值
node<T>* tmp=ptr; //拷贝一个ptr的副本,因为我们是输出该位置上的数值之后删除,如果输出的时候动了ptr指针,等下删除没法正常进行
while(pos--) {
tmp=tmp->next;
if(tmp==head) pos++;
}
return tmp->data;
}
bool is_Empty() {return n == 0;}
~CircleList() { //析构函数
node<T>* p=head->next;
node<T>* q;
while(p != head) {
q=p->next;
delete p;
p=q;
}
delete head;
n = 0;
}
};
设计好了一个恰当的类,将对主函数实现起到莫大的作用:
//简短的主函数
int main() {
int n,m;
cin>>n>>m;
CircleList<int> v;
for(int i=1;i<=n;i++) v.push(i);
int cnt=1;
while(!v.is_Empty()) {
if(cnt==m+1) cnt=1;
if(cnt == m) {
cout<<v.At(cnt)<<" ";
v.Delete(cnt);
}
cnt++;
}
return 0;
}