1、循环链表的实现
什么是循环链表?
-概念上
任意数据元素都有—个前驱和—个后维
所有的数据元素的关系构成—个逻辑上的环
-实现上
循环链表是一种特殊的单链表
尾结点的指针域保存了首结点的地址
循环链表的逻辑构成
循环链表的继承层次结构
循环链表的实现思路
-通过模板定义CircleList类,维承自LinkList类
-定义内部函数last_to_first() , 用于将单链表首尾相连
-特殊处理:首元素的插入操作和删除操作
- 重新实现:清空操作和遍历操作循环链表的实现要点
-插入位置为0时:
头结点和尾结点均指向新结点
新结点成为首结点插入链表
-删除位置为0时:
头结点和尾结点指向位置为1的结点
安全销毁首结点
2、编程实验
循环链表的实现 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;
- Node* last() const //O(n)
- {
- return this->position(this->m_length-1)->next; //返回尾结点地址
- }
- void last_to_first() const //O(n)
- {
- last()->next = this->m_header.next;
- }
- int mod(int i) const //O(1)
- {
- return (this->m_length == 0) ? 0 : (i % this->m_length);
- }
- public:
- bool insert(const T& e) //O(n)
- {
- return insert(this->m_length,e);
- }
- bool insert(int i,const T& e) //O(2n+1) ==> O(n)
- {
- bool ret = true;
- i = i % (this->m_length + 1); //O(1)
- ret = LinkList<T>::insert(i,e); //先正常插入 O(n)
- if(ret && (i == 0))
- {
- last_to_first(); //O(n)
- }
- return ret;
- }
- bool remove(int i) //O(n)
- {
- 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(); //O(n)
- if(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); //O(n)
- }
- return ret;
- }
- bool set(int i,const T& e) //O(n)
- {
- return LinkList<T>::set(mod(i),e);
- }
- T get(int i) const //O(n)
- {
- return LinkList<T>::get(mod(i));
- }
- bool get(int i,const T& e) const //O(n)
- {
- return LinkList<T>::get(mod(i),e);
- }
- /* int find(const T& e)
- {
- int ret = -1;
- last()->next = NULL; //变为单链表
- ret = LinkList<T>::find(e);
- last_to_first();
- return ret;
- }
- */
- /*
- * 不安全,直接调用父类的find函数,在" == "比较时有可能
- * 比较的是对象,即需要重载函数,可能抛异常,无法进行
- * last_to_first操作,循环链表状态直接被改变
- */
- int find(const T& e) //O(n)
- {
- 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()
- {
- if(this->m_length > 0)
- {
- last()->next = NULL;
- LinkList<T>::clear();
- }
- }
- */
- void clear() //O(n)
- {
- while(this->m_length > 1) //O(n)
- {
- remove(1);//不remove(0)的原因是效率 //O(1)
- }
- if(this->m_length == 1) //O(1)
- {
- Node* toDel = this->m_header.next;
- this->m_header.next = NULL;
- this->m_current = NULL;
- this->m_length = 0;
- this->destroy(toDel);
- }
- }
- bool move(int i,int step) //O(n)
- {
- return LinkList<T>::move(mod(i),step);
- }
- bool end() //O(1)
- {
- return ( (this->m_length == 0) || (this->m_current == NULL) );
- }
- ~CircleList() //O(n)
- {
- clear();
- }
- };
- }
- #endif // CIRCLELIST_H
修改LinkList.h
- #ifndef LINKLIST_H
- #define LINKLIST_H
- #include"List.h"
- #include"Exception.h"
- namespace DTLib
- {
- template <typename T>
- class LinkList : public List<T>
- {
- protected:
- struct Node : public Object
- {
- T value;
- Node* next;
- };
- mutable struct : public Object //保证内存布局一致
- {
- char reserved[sizeof(T)]; //木有实际作用,仅仅占空间,保证内存布局和Node一样
- Node* next;
- }m_header;
- int m_length;
- Node* m_current;
- int m_step;
- Node* position(int i) const //定位到i的前一个结点位置
- {
- Node* ret = reinterpret_cast<Node*>(&m_header);
- for(int p=0;p<i;p++)
- {
- ret = ret->next;
- }
- return ret;
- }
- virtual Node* create()
- {
- return new Node();
- }
- virtual void destroy(Node* pn)
- {
- delete pn;
- }
- public:
- LinkList()
- {
- m_header.next = NULL;
- m_length = 0;
- m_step = 1;
- m_current = NULL;
- }
- bool insert(int i,const T& e)
- {
- bool ret = (0 <= i)&&(i <= m_length);
- if(ret)
- {
- Node* node = create();
- if( node )
- {
- Node* current = position(i);
- node->value = e;
- node->next = current->next;
- current->next = node;
- m_length++;
- }
- else
- {
- THROW_EXCEPTION(NoEnoughMemoryException,"No memory to insert new element ...");
- }
- }
- return ret;
- }
- bool insert(const T& e)
- {
- return insert(m_length,e);
- }
- bool remove(int i)
- {
- bool ret = (0 <= i)&&(i < m_length);
- if(ret)
- {
- Node* current = position(i);
- Node* toDel = current->next;
- if(m_current == toDel)
- {
- m_current = toDel->next; //游标指向下一个数据元素即可
- }
- current->next = toDel->next;
- m_length--;
- destroy(toDel);
- }
- return ret;
- }
- bool set(int i,const T& e)
- {
- bool ret = (0 <= i)&&(i < m_length);
- if(ret)
- {
- position(i)->next->value = e;
- }
- return ret;
- }
- virtual T get(int i) const
- {
- T ret;
- if(get(i,ret))
- {
- return ret;
- }
- else
- {
- THROW_EXCEPTION(IndexOutOfBoundsExpception,"Invalid parameter i to get element ...");
- }
- return ret;
- }
- bool get(int i,T& e) const
- {
- bool ret = (0 <= i)&&(i < m_length);
- if(ret)
- {
- e = position(i)->next->value; //position 为 const 成员函数
- }
- return ret;
- }
- int find(const T& e) const
- {
- int ret = -1;
- int i = 0;
- Node* node = m_header.next;
- while( node )
- {
- if(node->value == e)
- {
- ret = i;
- break;
- }
- else
- {
- node = node->next;
- i++;
- }
- }
- return ret;
- }
- int length() const
- {
- return m_length;
- }
- void clear()
- {
- while( m_header.next )
- {
- Node* toDel = m_header.next;
- m_header.next = toDel->next;
- m_length--;
- destroy(toDel);
- }
- }
- virtual bool move(int i,int step = 1)
- {
- bool ret = (0 <= i)&&(i < m_length)&&(step > 0);
- if(ret)
- {
- m_current = position(i)->next;
- m_step = step;
- }
- return ret;
- }
- virtual bool end()
- {
- return ( m_current == NULL);
- }
- virtual T current()
- {
- if(!end()) //if( m_current != NULL )
- {
- return m_current->value;
- }
- else
- {
- THROW_EXCEPTION(InvalidParameterException,"No value at current position ...");
- }
- }
- virtual bool next()
- {
- int i = 0;
- while((i < m_step) && !end())
- {
- m_current = m_current->next;
- i++;
- }
- return (i == m_step);
- }
- ~LinkList()
- {
- clear(); //构造函数析构函数不会发生多态,destroy调用的父类
- }
- };
- }
- #endif // LINKLIST_H
3、循环链表的应用
-约瑟夫环问题
已知n个人(以编号0, 1 , 2 , 3 ,…,n-1分别表示)围坐在—张圆桌周围。
从编号为k的人开始报数,数到m的那个人出列;他的 下—个人又从1开始报数,
数到m的那个人又出列;依此规律重复 下去,直到圆桌周围的入全部出列。
小故事
在罗马人占领乔塔帕特后,39个犹太人与 Josephus及他的朋友躲到一个洞中,39个犹大人
决定宁愿死也不要被敌人抓到,于是决定了一个 自杀方式,41个人排成一个圆圈,由第1个人开始
报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。
然而Josephus和他的朋友并不想遵从。那么,一开始要站在什么地方才能避免被处决?
4、编程实验
约瑟夫问题 main.cpp- #include <iostream>
- #include"CircleList.h"
- using namespace std;
- using namespace DTLib;
- 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);
- while(cl.length() > 0)
- {
- cl.next();
- cout<<cl.current()<<" ";
- cl.remove(cl.find(cl.current()));
- }
- }
- int main()
- {
- josephus(41,1,3);
- return 0;
- }
5、小结
循环链表是—种特殊的单链表
尾结点的指针域保存了首结点的地址
特殊处理首元素的插入操作和删除操作
重新实现清空操作和追历操作