1、什么是循环链表
— 概念上:
1、任何数据元素都有一个前驱和一个后继
2、所有的数据元素的关系构成一个逻辑的环
— 实现上:
1、循环链表是一种特殊的单链表
2、尾结点的指针域保存了首结点的地址
2、循环链表的逻辑构成
3、循环链表的继承层次结构
4、循环链表的实现思路
Node* last() //指向尾结点
{
return this->position(this->m_length-1)->next;
}
void last_to_first()
{
last()->next = this->m_header.next;
}
5、循环链表的实现要点
— 插入位置为 0 时:
bool insert(int i, const T& e) //相比较前面插入,多了一个在插入位置为 0 时尾结点指向首结点的操作。
{
bool ret = true;
i = i % (this->m_length + 1); //循环链表,所以 i 的值可能非常大,要取余。 + 1 是为了能插入到尾结点和首结点之间的位置
ret = ret && LinkList<T>::insert(i, e);
if(ret && i == 0)
{
last_to_first();
}
return ret;
}
— 删除位置为 0 时:
bool remove(int i)
{
bool ret = true;
i = mod(i);
if(i == 0) //删除位置为0
{
Node* toDel = this->m_header.next;
if(toDel != nullptr) //链表长度大于等于1
{
this->m_header->next = toDel->next;
this->m_length--;
if(this->m_length > 0) //删除位置为0后还有结点
{
last_to_first();
if(this->m_current == toDel)
{
this->m_current = toDel->next;
}
}
else
{
this->m_header->next = nullptr;
this->m_current = nullptr;
}
this->destroy(toDel);
}
else
{
ret = false;
}
}
else
{
ret = LinkList<T>::remove(i);
}
return ret;
}
实现:
CircleList.h
#ifndef CIRCLELIST_H
#define CIRCLELIST_H
#include "LinkList.h"
namespace XiebsLib
{
template <typename T>
class CircleList : public LinkList<T>
{
protected:
typedef typename LinkList<T>::Node Node;
int mod(int i)const
{
return (this->m_length == 0 ? 0 : i % this->m_length);
}
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(int i, const T& e)
{
bool ret = true;
i = i % (this->m_length + 1);
ret = LinkList<T>::insert(i, e);
if(ret && i == 0)
{
last_to_first();
}
return ret;
}
bool insert(const T& e)
{
return LinkList<T>::insert(this->m_length, e);
}
bool remove(int i)
{
bool ret = true;
i = mod(i);
if(i == 0)
{
Node* toDel = this->m_header.next;
if(toDel != nullptr)
{
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 = toDel->next;
}
}
else
{
this->m_header.next = nullptr;
this->m_current = nullptr;
}
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);
}
bool get(int i, T& e)const
{
return LinkList<T>::get(mod(i), e);
}
T get(int i)const
{
return LinkList<T>::get(mod(i));
}
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;
}
void clear()
{
while(this->m_length > 1)
{
remove(1); //效率最大化
}
if(this->m_length == 1)
{
Node* toDel = this->m_header.next;
this->m_header.next = nullptr;
this->m_length = 0;
this->m_current = nullptr;
this->destroy(toDel);
}
}
bool move(int i, int step)
{
return LinkList<T>::move(mod(i), step);
}
bool end()
{
return (this->m_length == 0) || (this->m_current == nullptr);
}
~CircleList()
{
clear();
}
};
}
#endif // CIRCLELIST_H
6、循环链表的应用
— 约瑟夫环问题
- 小故事
#include <iostream>
#include "CircleList.h"
#include "Exception.h"
using namespace std;
using namespace XiebsLib;
void josephus(int n, int s, int 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;
}
- 小结
— 循环链表是一种特殊的单链表
— 尾结点的指针域保存了首结点的地址
— 特殊处理首元素的插入操作和删除操作
— 重新实现清空操作和遍历操作