一、问题重述:
n 个人围成一个圆圈,首先第 1 个人从 1 开始,一个人一个人顺时针报数, 报到第 m 个人,令其出列。然后再从下一 个人开始,从 1 顺时针报数,报到第 m 个人,再令其出列,…,如此下去, 直到圆圈中只剩一个人为止。此人即为优胜者,请输出优胜者。
二、实现思路:
将结点的数据域存储元素的序号,通过链表的遍历、元素的查询(search)、元素的删除(remove)等方法便可以较容易实现。
三、下面是具体代码实现思路:
首先是单向循环链表的实现:
enum InsMod {INF, INR};//头插还是尾插
template <typename T>class CircLinkNode{
public:
T data;
CircLinkNode<T> *link;
CircLinkNode(CircLinkNode<T> *ptr = NULL){//建立空结点
link = ptr;
}
CircLinkNode(const T &item, CircLinkNode<T> *ptr = NULL){//建立非空结点
data = item;
link = ptr;
}
};
template <typename T>class CircList{
public:
CircList()
{
this->first = new CircLinkNode<T>();
first->link=first;
last=first;
}
CircList(CircList<T> &L)//复制一个环链表
{
CircLinkNode<T>* p = L.first->link;
CircLinkNode<T>*q = this->first->link;
while(p!=first)
{
q= new CircLinkNode<T>(p->data);
q=q->link;
p=p->link;
}
last = q;
}
~CircList()
{
this->makeEmpty();
delete this->first;
}
void makeEmpty()
{
CircLinkNode<T>*p = this->first->link;
CircLinkNode<T>*q = p->link;
while(p!=first)
{
delete p;
p = q;
q=q->link;
}
}
int Length()const
{
int num =1;
CircLinkNode<T>* p = first->link;
while(p!=first)
{
++num;
p=p->link;
}
return num;
}
CircLinkNode<T> *Search(T& x,int &n)
{
CircLinkNode<T>* p = first->link;
n=1;
while(p->data!=x)
{
++n;
p=p->link;
if(p==first)
exit(1);
}
return p;
}
CircLinkNode<T> *Locate(int i)
{
if(i<1||i>Length())
{
exit(1);
}
int num=0;
CircLinkNode<T>*p = first->link;
while(p!=first)
{
++num;
if(num==i)
return p;
p=p->link;
}
return p;
}
bool getData(int i,T&x)const
{
if(i<1||i>Length())
{
return false;
}
int num=1;
CircLinkNode<T>*p = first->link;
while(p!=first)
{
++num;
if(num==i)
x = p->data;
p=p->link;
}
return true;
}
void setData(int i, T &x)
{
if(i<1||i>Length())
{
return;
}
int num=0;
CircLinkNode<T>*p = first->link;
while(p!=first)
{
++num;
if(num==i)
p->data=x;
p=p->link;
}
}
bool Insert(int i, T &x)
{
if(i<1||i>Length()+1)
{
return false;
}
CircLinkNode<T>* p =NULL;
if(i==1)
p = first;
else
p = Locate(i-1);
CircLinkNode<T>* q = p->link;
CircLinkNode<T> *newNode = new CircLinkNode<T>(x);
p->link = newNode;
newNode->link = q;
if(i==Length()+1)
{
last = newNode;
}
return true;
}
bool Remove(int i, T &x)
{
if(i<=0||i>Length())
{
return false;
}
int j=0;
CircLinkNode<T>* p = first;
for(j=1; j<i; j++)
{
p=p->link;
}
CircLinkNode<T> *q = p->link->link;
x = p->link->data;
if(i==Length())
last = p;
delete p->link;
p->link = q;
return true;
}
bool IsEmpty()const
{
return first->link==last;
}
bool IsFull()const
{
return (new CircLinkNode<T>() )==NULL;
}
void Sort()
{
CircLinkNode<T> * p =first->link;
CircLinkNode<T> *q=NULL;
if(NULL==p)
return ;
int num = this->Length();
int maxIndex=1;
int Max =p->data;
for(int i=1; i<num; i++)
{
int j=1;
for(; j<num-i+2; j++)
{
if(Max<=p->data)
{
maxIndex = j;
Max = p->data;
}
q=p;
p=p->link;
}
setData(maxIndex,q->data);
setData(num,Max);
}
}
void Inverse()//不要返回
{
int num = this->Length();
for(int i=1; i<num/2+1; i++)
{
int j=num-i+1;
CircLinkNode<T> * p = Locate(i);
CircLinkNode<T>* q = Locate(j);
T temp = p->data;
p->data = q->data;
q->data = temp;
}
}
void input(T endTag, InsMod im = INR)
{
if(im==INR)
{
Insert(Length()+1,endTag);
}
else{
Insert(1,endTag);
}
}
void output()
{
CircLinkNode<T>* p =first->link;
while(p!=first)
{
cout<<p->data<<" ";
p=p->link;
}
cout<<endl;
}
CircList<T> &operator = (CircList<T> &L)
{
this->makeEmpty();
CircLinkNode<T>* q = L.first->link;
while(q!=L.first)
{
input(q->data,INR);
q=q->link;
}
}
CircLinkNode<T>* travel(int x,CircLinkNode<T>*temp)
{
CircLinkNode<T>* pre = temp;
for(int i=1;i<x;i++)
{
if(pre==first)
--i;
pre=pre->link;
}
if(pre==first)
pre=pre->link;
return pre;
}
int getNum(CircLinkNode<T>* temp)
{
CircLinkNode<T>*pre = first->link;
int num=1;
while(pre!=temp)
{
if(pre!=first)
++num;
pre=pre->link;
}
return num;
}
friend ostream& operator << (ostream &out, CircList<T> &L){
CircLinkNode<T> *current = L.first->link;
while (current != L.first){
out << current->data <<'\t';
current = current->link;
}
out<<endl;
return out;
}
friend istream& operator >> (istream &in, CircList<T> &L){//重新输入数据,向后生成
T val;
L.makeEmpty();//先清空
while (!in.eof()){
in >> val;
L.last->link = new CircLinkNode<T>(val);
assert(L.last->link);
L.last = L.last->link;
}
L.last->link = L.first;
in.clear();//当以ctrl_Z结束,流关闭,必须重新打开
return in;
}
CircLinkNode<T>* getFirst()
{
return first;
}
protected:
CircLinkNode<T> *first, *last;
void inputFront(T endTag);
void inputRear(T endTag);
};
根据约瑟夫环的要求,在链表类内加入travel()方法返回每次遍历要出列的人。由于笔者采用的是带头结点的链表,在遍历时必然会遇到空头节点,此时让迭代次数减一以保证遍历的正确。
CircLinkNode<T>* travel(int x,CircLinkNode<T>*temp)
{
CircLinkNode<T>* pre = temp;
for(int i=1;i<x;i++)
{
if(pre==first)
--i;
pre=pre->link;
}
if(pre==first)
pre=pre->link;
return pre;
}
在链表类内加入getNum()实现每次循环后,得到要查询结点此时在链表里的相对序号。
int getNum(CircLinkNode<T>* temp)
{
CircLinkNode<T>*pre = first->link;
int num=1;
while(pre!=temp)
{
if(pre!=first)
++num;
pre=pre->link;
}
return num;
}
在main函数里具体实现如下:
int main(){
CircList<int> List;
int x=0;
for(int i=0;i<10;i++)//假设约瑟夫环最初有十人,序号为从1到10
{
x=i+1;
List.Insert(i+1,x);
}
CircLinkNode<int>* pre = List.getFirst()->link;//初始从序号为1的人开始循环
CircLinkNode<int>* prev =pre;//中间指针,便于pre结点删除后能够赋值给pre继续循环
while(List.Length()!=1)
{
pre= List.travel(3,pre);//循环设为3
prev = pre;
pre=pre->link;
List.Remove(List.getNum(prev),x);
cout<<"出列的人为:"<<x<<endl;
}
List.getData(1,x);
cout<<"ViCTOR is :"<<x<<endl;
return 0;
}