29、 循环链表

概念上:任意数据元素都有一个前驱后一个后继,所有的数据元素的关系构成一个逻辑上的环。

实现上:循环链表是一种特殊的单链表,尾节点的指针域保存了首结点的地址。

 循环链表的实现思路:

通过模板定义CircleList类,继承自LinkList类,定义内部函数last_to_first(),用于将单链表首尾相连。

特殊处理:首元素的插入操作和删除操作。

重新实现:清空操作和遍历操作。

循环链表的实现要点:插入位置为0时:头结点和尾结点均指向新结点。新结点成为首结点插入链表。

删除位置为0时:头结点和尾结点指向位置为1的结点,安全销毁首结点。(先移动两个指针,销毁首结点)

单向循环链表:表尾节点存储的指针不为NULL,而是该链表表头节点的指针。应用:

循环链表的应用:约瑟夫环问题,那个人,从k开始报数,数到m的那个人出列,下一个又从一开始,直到全部出列。

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%(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(const T& e)
{
return insert(this->m_length,e);
}
bool insert(int i, const T& e)
{
bool ret=true;
i=i%(this->m_length+1);//想要用父类的insert实现子类的insert
ret=LinkList<T>::insert(i,e);
if(ret&&(i==0))
{
last_to_first();
}
return ret;
}
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=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);
}
T get(int i)
{
return LinkList<T>::get(mod(i));
}
T get(int i,const T& e) const
{
return LinkList<T>::(mod(i),e) //mod 函数如果不是const函数的话,不对,const函数只能调用const函数
}
int find(const T& e) const
{
int ret=-1;
/*last()->next=NULL;
tet=LinkList<T>::find(e); //不对,变成了单链表,父类中有==,如果是类类型的话得重载==,万一抛出异常,下边语句就调用不到了,循环链表变为单链表,不是异常安全的
last_to_first();*/
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);//比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=1)  //move(0)表示position(0)头结点->next,定位到第0个元素
{
return LinkList<T>::move(mod(i),step);
}
bool end()
{
return (this->m_length==0||this->m_current==NULL);
//return m_current.isNull();
}


~CircleList()
{
clear();
}
};
}

扫描二维码关注公众号,回复: 1096510 查看本文章

#endif

main:函数

void josephus(int n, int s, int m)//n个人,从s开始,第m个人出列
{
CircleList<int>c1;
for(int i=1;i<=n;i++)
{
c1.insert(i);
}
c1.move(s-1,m-1);  //移动到s-1,数m-1个,到3移动2次报数到m,就数到m-1
while(c1.length()>0)
{
c1.next();
cout<<c1.current()<<endl;
c1.remove(c1.find(c1.current())); 
}
}
int main()
{
//josephus(41,1,3);
CircleList<int>c2;
for(int i=1;i<=41;i++)
{
c2.insert(i);
}
c2.move(3+41,1);
cout<<c2.current()<<endl;
c2.move(3,1);
cout<<c2.current()<<endl;
return 0;
}
//注意insert中i=i%(this->m_length+1);表示从0到数据长度的位置都可以插入数据,mod函数中只是i%(m_length))而减1是在main中c1.move(s-1,m-1)实现的

小结:循环链表是一种特殊的单链表,尾节点的指针保存了首结点的地址,特殊处理首元素的插入操作和删除操作,重新实现清空操作和遍历操作。

双向循环链表:每个节点都存储了两个指针,一个指向前驱节点,另一个指向后驱节点。一个双向循环链表有一个表头节点,他同样有两个指针,一个指向链表的最后一个节点,一个指向链表最前端的第一个节点x(0)。

数组和链表都是线性数据结构。 

猜你喜欢

转载自blog.csdn.net/ws857707645/article/details/80374751
29
今日推荐