约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围,从编号为k的人开始报数,数到m的那个人出列,它的下一个人又从1开始报数,数到m的那个人出列,一次规律重复下去,直到圆桌周围的人全部出列,输出人的出列顺序。
解题思路:
代码实现:
结构体实现:
#include
#include
#include
using namespace std;
typedef int SLTDatatype;
typedef struct SListNode
{
SLTDatatype _data;
struct SListNode* _next;
}SListNode;
typedef struct SList
{
struct SListNode* _head;
}SList;
SListNode* BuySListNode(SList* plist, SLTDatatype x)
{
SListNode* newnode = new SListNode;
newnode->_data = x;
newnode->_next = plist->_head;
return newnode;
}
void SListInit(SList* plist)
{
assert(plist);
plist->_head = new SListNode;
plist->_head->_data = 0;
plist->_head->_next = plist->_head;
}
void insertTail(SList* plist, int val)
{
SListNode* last = plist->_head;
while (last->_next != plist->_head)
last = last->_next;
last->_next = BuySListNode(plist, val);
}
void show(SList* plist, int p, int k, int m)
{
SListNode* prev = plist->_head;
SListNode* cur = plist->_head->_next;
//先让cur指向第k个人
while (k-- > 1)
{
prev = cur;
cur = cur->_next;
}
//循环进行报数,然后出列
for (; p > 0; --p)
{
if (plist->_head->_next == plist->_head)
return;
//报数之前,检查一个cur,一定要指向一个有效的人
if (cur == plist->_head)
{
prev = cur;
cur = cur->_next;
}
//报数m的过程
for (int i = 1; i < m; ++i)
{
prev = cur;
cur = cur->_next;
if (cur == plist->_head)
{
prev = cur;
cur = cur->_next;
}
}
//删除cur节点
cout << cur->_data << " 出列!" << endl;
prev->_next = cur->_next;
cur = cur->_next;
}
}
void test()
{
int pnum, knum, mnum;
cout << "输入人的个数:" << endl;
cin >> pnum;
cout << "输入每次开始报数的人的序号:" << endl;
cin >> knum;
cout << "输入每次报数的次数:" << endl;
cin >> mnum;
SList s1;
SListInit(&s1);
for (int i = 1; i <= pnum; ++i)
insertTail(&s1, i);
show(&s1, pnum, knum, mnum);
}
C++类实现:
class Josephus
{
public:
Josephus()
{
_head = new SListNode(0, nullptr);
_head->_next = _head;
}
void InsertTail(int val)
{
SListNode* last = _head;
while (last->_next != _head)
last = last->_next;
last->_next = new SListNode(val, _head);
}
void show(int k, int m)
{
SListNode* prev = _head;
SListNode* cur = _head->_next;
//让cur指向第k个人
while (k-->1)
{
prev = cur;
cur = cur->_next;
}
//循环进行报数,然后出列
while (_head->_next != _head)
{
//报数前,检查cur,cur一定要指向一个有效的人
if (cur == _head)
{
prev = cur;
cur = cur->_next;
}
//报数的过程
for (int i = 1; i < m; ++i)
{
prev = cur;
cur = cur->_next;
if (cur == _head)
{
prev = cur;
cur = cur->_next;
}
}
cout << cur->_data << " 出列!" << endl;
prev->_next = cur->_next;
cur = prev->_next;
}
}
class SListNode
{
public:
int _data;
SListNode* _next;
SListNode(int data, SListNode* next)
:_data(data)
, _next(next)
{ }
};
SListNode* _head;
};
void test()
{
int pnum, knum, mnum;
cout << "输入人的个数:" << endl;
cin >> pnum;
cout << "输入每次开始报数的人的序号:" << endl;
cin >> knum;
cout << "输入每次报数的次数:" << endl;
cin >> mnum;
Josephus* j = new Josephus();
for (int i=1; i <= pnum; ++i)
j->InsertTail(i);
j->show(knum, mnum);
}
解题思路:
代码实现:
class Josephus
{
public:
Josephus()
{
_first = nullptr;
}
void InsertTail(int val)
{
if (_first == nullptr)
{
_first = new SListNode(val, nullptr);
_first->_next = _first;
}
else
{
SListNode* last = _first;
while (last->_next != _first)
last = last->_next;
last->_next = new SListNode(val, _first);
}
}
void show(int k, int m)
{
SListNode* prev = _first;
SListNode* cur = _first;
//因为初始时cur是第一个节点,那么prev就是最后一个节点
while (prev->_next != _first)
prev = prev->_next;
//让cur指向第k个人
while (k-- > 1)
{
prev = cur;
cur = cur->_next;
}
//循环进行报数,然后出列
while (cur!=prev)//当cur==prev时,表示只剩下最后一个节点
{
//报数的过程
for (int i = 1; i < m; ++i)
{
prev = cur;
cur = cur->_next;
}
cout << cur->_data << " 出列!" << endl;
prev->_next = cur->_next;
cur = prev->_next;
}
cout << cur->_data << " 出列!" << endl;
_first = nullptr;
}
class SListNode
{
public:
int _data;
SListNode* _next;
SListNode(int data, SListNode* next)
:_data(data)
, _next(next)
{ }
};
SListNode* _first;//指向链表的第一个节点
};
void test()
{
int pnum, knum, mnum;
cout << "输入人的个数:" << endl;
cin >> pnum;
cout << "输入每次开始报数的人的序号:" << endl;
cin >> knum;
cout << "输入每次报数的次数:" << endl;
cin >> mnum;
Josephus* j = new Josephus();
for (int i = 1; i <= pnum; ++i)
j->InsertTail(i);
j->show(knum, mnum);
}
这是单向循环链表的一个应用场景,把握好出队删除节点时prev和cur的指向,以及处理好带头节点与不带头节点时循环的条件即可。