文章目录
CircleList.h
#ifndef CIRCLELIST_H
#define CIRCLELIST_H
/*
*/
#include "LinkList.h"
namespace DTLib
{
template < typename T >
class CircleList : public LinkList<T>
{
protected:
typedef typename LinkList<T>::Node Node;
// typedef 简化一个类型,给一个类型新的名字 Node
// 父类的Node和泛指类型和泛指类型牵扯上了关系,此处不可用,故而需要重新定义
// LinkList<T>::Node 编译器不知道这是类型还是静态成员变量,需要关键字typename出场
// 不能对0取余, 需要特殊处理
int mod(int i) const
{
return (this->m_length == 0) ? 0 : (i % this->m_length);
}
// last获取最后一个节点的指针
// position定位最后一个位置,再取next可以获取最后一个节点的指针
Node* last() const
{
return this->position(this->m_length - 1)->next;
}
void last_to_first() const
{
last()->next = this->m_header.next;
}
public:
bool insert(const T &e)
{
return insert(this->m_length, e);
}
// 在位置为0处插入节点: 移动头节点指针和尾节点指针指向新节点,新节点的指针指向原来的首节点
bool insert(int i, const T &e)
{
bool ret = true;
i = i % (this->m_length + 1); // 想要调用父类的insert来实现子类的insert,这就是为什么要取余的原因
ret = LinkList<T>::insert(i, e); //要用父类的insert函数实现子类的insert函数
if(ret && (i == 0))
{
last_to_first();
}
return ret;
}
// 删除位置为0的节点: 移动头节点指针和尾节点指针指向要删除节点的下一个节点(1号节点), 销毁节点,重新编号
bool remove(int i)
{
bool ret = true;
i = mod(i);
if(i == 0)
{
Node* toDel = this->m_header.next; // 定义局部指针指向即将删除的节点(首节点)
if( toDel != NULL )
{
this->m_header.next = toDel->next; // 异常安全
this->m_length--;
if( this->m_length > 0 ) // 在还有元素的情况下才能移动指针
{
last_to_first();
if( this->m_current == toDel ) // 如果删除的时候刚好是this->m_current节点,移动一下指针
{
this->m_current = toDel->next;
}
}
else
{
this->m_header.next = NULL;
this->m_current = NULL;
}
this->destroy(toDel);
}
else
{
ret = false;
}
}
else
{
ret = LinkList<T>::remove(i); // 删除非首节点
}
return ret;
}
bool set(int i, const T& e)
{
return LinkList<T>::set(mod(i), e); // 注意: 由于是循环列表,i有可能非常大的一个值,要取余
}
T get(int i) const
{
return LinkList<T>::get(mod(i));
}
T get(int i, T& e) const
{
return LinkList<T>::get(mod(i), e);
}
// 用指针遍历每一个数据元素
int find(const T &e) const
{
int ret = -1;
Node* slider = this->m_header.next;
for(int i=0; i<this->m_length; i++)
{
if(slider->value == e)
{
ret = i;
break;
}
slider = slider->next;
}
return ret;
/* 这样做可以吗? 先变成单链表,再变成循环列表,如此精妙?
last()->next = NULL;
ret = LinkList<T>::find(e);
last_to_first();
*
* 这样做是不对的:
* 分析:
* 改变了循环列表的状态,在LinkList.h中分析
int find(const T& e) const
{
int ret = -1;
int i=0;
Node* node = m_header.next;
while(node != NULL)
{
if(node->value == e) // 是泛指类型所对应的值之间的比较,T可能是个类类型,要比较,肯定要重载操作符
{ // 如果在相等操作符重载中抛出了异常,会怎样?
// 会异常返回 到 ret = LinkList<T>::find(e);
// 这里没有 try...catch(), 异常会接着返回,
// last_to_first() 就没有机会被调用了,
// find函数出了异常,循环链表就变成单链表了,改变了链表的状态和特性
// (为了很好的移植性,不允许使用try...catch())
//
ret = i;
break;
}
else
{
node = node->next;
i++;
}
}
return ret;
}
*
*
*/
}
void clear()
{
while( this->m_length > 1 )
{
remove(1); // 节点个数大于1,那就删掉下标为1的节点(效率高)
// 如果每次都是 remove(0),每次都要调用last_to_first();内部还有很多调用
// 会导致大量的移动
}
if( this->m_length == 1)
{
Node* toDel = this->m_header.next;
this->m_header.next = NULL;
this->m_length = 0;
this->m_current = NULL;
this->destroy(toDel);
}
/* 这样做可以吗?
if( this->m_length > 0 )
{
last()->next = NULL;
LinkList<T>::clear(); // 道理一样的,clear()如果发生了异常,当前的链表就不再是循环链表了
}
*/
}
bool move(int i, int step) // 重新实现遍历操作,把父类的遍历相关操作变成虚函数,需要实现就就两个
{
return LinkList<T>::move(mod(i), step);
}
bool end()
{
return (this->m_length == 0) || (this->m_current == NULL);
}
~CircleList()
{
clear();
}
};
}
#endif // CIRCLELIST_H
main.cpp
#include <iostream>
#include "CircleList.h"
#include "Exception.h"
using namespace std;
using namespace DTLib;
void josephus(int n, int s, int m) //n有多少个人玩这个游戏,s从第几个人开始报数,m报到多少
{
CircleList<int> cl;
for(int i = 1; i <= n; i++)
{
cl.insert(i);
}
cl.move(s-1, m-1); //n-1是因为要从下标为0开始,m-1是因为1到3之间隔了两个,移动两次
while(cl.length() > 0) // 重复自杀的过程
{
cl.next();
cout << cl.current() << endl;
cl.remove(cl.find(cl.current())); //删除自杀这个人的下标
}
}
int main()
{
josephus(41, 1, 3); // 最后出来俩位置,,不会死的位置
return 0;
}