前言
初学数据结构,关于循环链表,目前还在表层,特以约瑟夫环为例学习循环链表,全文仅供参考,如有不足的地方还望多多指教。
提示:以下是本篇文章正文内容,下面案例可供参考
一、约瑟夫环
据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
二、关于循环链表
源自个人理解
1.一种线性表,这里讨论的是单循环链表(个人比较适应带头结点的单链表,一下进行的链表都是带头结点的链表);
2.单循环链表,其实就是将单链表的尾结点的next指针指向头结点;
3.单循环链表实现了结点不能访问直接前驱的不足,通过循环遍历,以访问到当前结点的前驱;
4.单循环链表的遍历需要注意在尾部设置暂停条件;
5.单循环链表在插入新结点时,使用尾插法时需要注意尾结点的next指针要指向头结点;
6.单循环链表在删除元素时,也要注意尾部结点删除时要注意next指针的指向;
7.综上,我在使用单循环链表时,也采用了建立尾指针指向尾结点,则有last->next = first;
1.基本前提
这里在创建结点时和定义类时,由于是解决约瑟夫环的问题,故可以不用模板,我这里仅供参考。
template<class DataType>
struct Node{
DataType data;
Node * next;
};//创建结点
template<class DataType>
class CirculateList{
public:
CirculateList();//无参构造
~CirculateList();//析构
void Insert(DataType elem);//插入
void Delete_elem(Node<DataType>* elem);//删除
void Display();//打印
int GetLength(){ return length; }//获取长度
void CirculateDelete();//循环删除(关键)
private:
Node<DataType> * space;//first
Node<DataType> * last;//尾指针
int length;//链表长度
};
2.无参构造函数
template<class DataType>
CirculateList<DataType>::CirculateList(){
length = 0;//设置长度为0
space = new Node<DataType>;//创建头结点
space->next = space;//初始化next指针指向
last = space;//初始化尾指针
}
3.析构函数
template<class DataType>
CirculateList<DataType>::~CirculateList(){
if (space == nullptr){
cout << "链表为空" << endl;
exit(0);
}
Node<DataType> * p = space->next; //指向链表的第一个有数据的结点
while (last != p)//该循环只删除了length-1个结点
{
Node<DataType> * q = p;
p = p->next;
delete q;//这里不能与上一句代码交换位置,不让会造成程序无法正常清除数据
//因为,如果先删除q,则会造成p->next无意义
}
delete last;//删除尾结点
delete space;//清除头结点
}
4.插入操作
template<class DataType>
void CirculateList<DataType>::Insert(DataType elem){
if (space == nullptr){
cout << "链表已被删除" << endl;
return;
}
Node<DataType> * temp = new Node<DataType>;
temp->data = elem;
//头插法
/*temp->next = p->next;
p->next = temp;*/
//尾插法(个人喜好尾插法)
temp->next = last->next;//让新结点的next指针指向头结点
last->next = temp;//连接新结点,使得新结点有直接前驱
last = temp;//last指向尾结点
length++;//元素个数加1
}
5.删除指定结点
//这里指定结点应该都是为链表的结点
template<class DataType>
void CirculateList<DataType>::Delete_elem(Node<DataType>* elem){
Node<DataType> * p = space;
while (last != p->next){
if (p->next == elem){
Node<DataType> * q = p->next;
p->next = q->next;//跳跃指向
delete q;
length--;
return;
}
p = p->next;
}
//针对尾结点的删除
if (last == p->next){
p->next = space;//注意循环条件
delete last;
last = p;//重新确定为指针
length--;
}
}
6.循环删除(关键步骤)
template<class DataType>
void CirculateList<DataType>::CirculateDelete(){
int count = 1;//控制循环报数
Node<DataType>* p = space->next;
/*
//测试代码,测试是否可以循环链表
初始条件:count = 100;
while (count){
if (last->next == p){
p = p->next;
}
cout << p->data << " ";
p = p->next;
count--;
}
cout << endl;
*/
while (length > 2)//保留最后两位,链表最终剩下两个结点
{
if (p == space)//如果p指针指向头结点,则移动p指针指向下一个结点的位置
{
p = p->next;
}
if (count == 3)//当报数报到3时就删除对应结点
{
Node<DataType>*q = p;
p = p->next;
//cout << "删除元素:" << q->data << endl;
Delete_elem(q);
//cout << "链表:";
//Display();
count = 1;
}
if (p == space)//针对删除last指向的结点后,指针p指向头结点,故需要将p移到下一个结点的
//位置
{
p = p->next;
}
else{
p = p->next;
count++;
}
}
}
7.打印链表
template<class DataType>
void CirculateList<DataType>::Display(){
if (length == 0){
cout << "List is empty" << endl;
return;
}
Node<DataType> * p = space->next;
while (last->next != p){
cout << p->data << " ";
p = p->next;
}
cout << endl;
}
三.完整代码展示
#include <iostream>
using namespace std;
template<class DataType>
struct Node{
DataType data;
Node * next;
};//创建结点
template<class DataType>
class CirculateList{
public:
CirculateList();//无参构造
~CirculateList();//析构
void Insert(DataType elem);//插入
void Delete(int i);//删除
void Delete_elem(Node<DataType>* elem);//删除
void Display();//打印
int GetLength(){ return length; }//获取长度
void CirculateDelete();//循环删除
private:
Node<DataType> * space;
Node<DataType> * last;
int length;
};
//无参构造
template<class DataType>
CirculateList<DataType>::CirculateList(){
length = 0;
space = new Node<DataType>;
space->next = space;
last = space;
}
//析构
template<class DataType>
CirculateList<DataType>::~CirculateList(){
if (space == nullptr){
cout << "链表为空" << endl;
exit(0);
}
/*Node<DataType> * p = space;
for (int i = 0; i < length + 1; ++i){
space = space->next;
delete p;
p = space;
}*/
Node<DataType> * p = space->next;
while (last != p){
Node<DataType> * q = p;
p = p->next;
delete q;
}
delete last;
delete space;
}
//插入
template<class DataType>
void CirculateList<DataType>::Insert(DataType elem){
if (space == nullptr){
cout << "链表已被删除" << endl;
return;
}
Node<DataType> * temp = new Node<DataType>;
temp->data = elem;
//头插法
/*temp->next = p->next;
p->next = temp;*/
//尾插法
temp->next = last->next;
last->next = temp;
last = temp;
length++;
}
//按位删除
template<class DataType>
void CirculateList<DataType>::Delete(int i){
if (space == nullptr){
cout << "List is empty" << endl;
return;
}
if (i<1 || i>length){
cout << "位置错误" << endl;
return;
}
int count = 0;
Node<DataType> * p = space;
while (count < i-1){
count++;
p = p->next;
}
Node<DataType>* temp = p->next;
p->next = temp->next;
length--;
}
//删除指定的结点
template<class DataType>
void CirculateList<DataType>::Delete_elem(Node<DataType>* elem){
Node<DataType> * p = space;
while (last != p->next){
if (p->next == elem){
Node<DataType> * q = p->next;
p->next = q->next;
delete q;
length--;
return;
}
p = p->next;
}
if (last == p->next){
p->next = space;
delete last;
last = p;
length--;
}
}
//打印链表
template<class DataType>
void CirculateList<DataType>::Display(){
if (length == 0){
cout << "List is empty" << endl;
return;
}
Node<DataType> * p = space->next;
while (last->next != p){
cout << p->data << " ";
p = p->next;
}
cout << endl;
}
//循环删除
template<class DataType>
void CirculateList<DataType>::CirculateDelete(){
int count = 1;
Node<DataType>* p = space->next;
/*
//测试代码,测试是否可以循环链表
初始条件:count = 100;
while (count){
if (last->next == p){
p = p->next;
}
cout << p->data << " ";
p = p->next;
count--;
}
cout << endl;*/
while (length > 2)//保留最后两位
{
if (p == space)//如果p指针指向头结点,则移动p指针指向下一个结点的位置
{
p = p->next;
}
if (count == 3)//当报数报到3时就删除对应结点
{
Node<DataType>*q = p;
p = p->next;
//cout << "删除元素:" << q->data << endl;
Delete_elem(q);
//cout << "链表:";
//Display();
count = 1;
}
if (p == space)//针对删除last指向的结点后,指针p指向头结点,故需要将p移到下一个结点的位置
{
p = p->next;
}
else{
p = p->next;
count++;
}
}
}
void main(){
CirculateList<int> head;
for (int i = 1; i <= 41; ++i){
head.Insert(i);//插入元素
}
cout <<"链表长度:"<< head.GetLength() << endl;
cout << "链表:";
head.Display();
//报数删除
head.CirculateDelete();
head.Display();//打印最后剩下的元素
system("pause");
}
最终效果:
第三行就是最终留下来的两个人的最初位置
感谢
通过学习广大学者的博客,自己最终写了这么一点东西,真心感谢。
心得
学习永无止境,前人的智慧仍然值得我们深思。
关于循环链表,个人只学习了浅薄知识,文中有问题的地方还望多多指出,第一次写博客,多多包涵。