目录
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
{
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
{
return (this->m_length == 0) ? 0 : (i % this->m_length);
}
public:
bool insert(int i, const T& e)
{
bool ret = true;
i = i % (this->m_length + 1);
ret = this->LinkList<T>::insert(i, e);
if( ret && i == 0)
{
last_to_first();
}
return ret;
}
bool insert(const T& e)
{
return 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 != 0)
{
this->m_header.next = toDel->next;
this->m_length--;
if(this->m_length > 0)
{
last_to_first();
if(toDel == this->m_current)
{
this->m_current = toDel->next;
}
}
else
{
this->m_header.next = 0;
this->m_current = 0;
}
this->destroy(toDel);
}
else
{
ret = false;
}
}
else
{
this->LinkList<T>::remove(i);
}
return ret;
}
bool set(int i, const T& e)
{
return this->LinkList<T>::set(mod(i), e);
}
T get(int i) const
{
return this->LinkList<T>::get(mod(i));
}
bool get(int i, T& e) const
{
return this->LinkList<T>::get(mod(i), e);
}
//不安全的做法:直接调用父类find函数,数据元素是对象时,重载的"=="可能抛异常,
//函数直接返回,无法调用last_to_first(),循环链表状态直接被改变
/*int find(const T& e) const
{
int ret = -1;
last()->next = 0;
ret = this->LinkList<T>::find(e);
last_to_first();
return ret;
}*/
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()
{
if(this->m_length > 0)
{
last()->next = 0;
this->LinkList<T>::clear();
}
}*/
void clear()
{
while(this->m_length > 1)
{
remove(1);//不remove(0)的原因是效率问题
}
if(this->m_length == 1)
{
Node* toDel = this->m_header.next;
this->m_header.next = 0;
this->m_current = 0;
this->m_length = 0;
this->destroy(toDel);
}
}
bool move(int i, int step)
{
return this->LinkList<T>::move(mod(i), step);
}
bool end()
{
return (this->m_length == 0) || (this->m_current == 0);
}
~CircleList()
{
clear();
}
};
}
#endif // CIRCLELIST_H
3、循环链表的应用
约瑟夫环问题
已知n个人(以编号0, 1 , 2 , 3 ,…,n-1分别表示)围坐在—张圆桌周围。
从编号为k的人开始报数,数到m的那个人出列;他的下—个人又从1开始报数,
数到m的那个人又出列;依此规律重复下去,直到圆桌周围的入全部出列。
小故事
在罗马人占领乔塔帕特后,39个犹太人与 Josephus及他的朋友躲到一个洞中,39个犹大人
决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始
报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。
然而Josephus和他的朋友并不想遵从。那么,一开始要站在什么地方才能避免被处决?
约瑟夫问题 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);//1 ~ 41
}
cl.move(s-1, m-1); //游标指向0处,步长2
while( cl.length() > 0 )
{
cl.next();
cout << cl.current() << " "; //当前要自杀的
cl.remove(cl.find(cl.current()));
}
}
int main()
{
josephus(41, 1, 3); //41个人,从1号开始数,数到第三个人开始自杀
return 0;
}
4、双向链表的实现
单链表的另—个缺陷
-单向性:只能从头结点开始高效访问链表中的数据元素
-缺陷:如果需要逆向访问单链表中的数据元素将极其低效
int main()
{
LinkList<int> l;
for(int i=0; i<5; i++) //O(n)
{
l.insert(0, i);
}
for(int i=l.length()-1; i>=0; i--) //O(n^2)
{
cout << l.get(i) << endl;
}
return 0;
}
新的线性表
在“单链表”的结点中增加指针pre, 用于指向当前结点的前驱结点。
- 双向链表是为了弥补单链表的缺陷而重新设计的
- 在概念上,双向链表不是单链表,没有继承关系
- 双向链表中的游标能够直接访问当前结点的前驱和后继
- 双向链表是线性表概念的最终实现(更贴近理论上的线性表)
双向链表的继承层次结构
5、编程实验
双向链表的实现 DualLinkList.h
#ifndef DUALLINKLIST_H
#define DUALLINKLIST_H
#include "List.h"
#include "Exception.h"
namespace DTLib
{
template <typename T>
class DualLinkList : public List<T>
{
protected:
struct Node : public Object
{
T value;
Node* next;
Node* pre;
};
mutable struct : public Object
{
char reserved[sizeof(T)];
Node* next;
Node* pre;
} m_header;
int m_length;
Node* m_current;
int m_step;
Node* position(int i) const
{
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:
DualLinkList()
{
m_header.next = 0;
m_header.pre = 0;
m_length = 0;
m_current = 0;
m_step = 1;
}
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* next = current->next;
node->value = e;
node->next = next;
current->next = node;
//第0个结点的的pre指针为NULL
if(current != reinterpret_cast<Node*>(&m_header))
{
node->pre = current;
}
else
{
node->pre = 0;
}
//不是尾部插入结点
if(next != 0)
{
next->pre = 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;
Node* next = toDel->next;
if(m_current == toDel)
{
m_current = next;
}
current->next = next;
if(next != 0)
{
next->pre = toDel->pre;
}
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;
}
bool get(int i, T& e) const
{
bool ret = (0 <= i) && (i < m_length);
if( ret )
{
e = position(i)->next->value;
}
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 ...");
}
}
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_length > 0)
{
remove(0);
}
}
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 == 0);
}
virtual T current()
{
if( !end() )
{
return m_current->value;
}
}
virtual bool next()
{
int i = 0;
while(i < m_step && !end())
{
m_current = m_current->next;
i++;
}
return i == m_step;
}
virtual bool pre()
{
int i = 0;
while(i < m_step && !end())
{
m_current = m_current->pre;
i++;
}
return i == m_step;
}
~DualLinkList()
{
clear();
}
};
}
#endif // DUALLINKLIST_H
main.cpp
#include <iostream>
#include "DualLinkList.h"
using namespace std;
using namespace DTLib;
int main()
{
DualLinkList<int> d1;
for(int i = 0; i < 5; i++)
{
d1.insert(0, i);
d1.insert(0, 5);
}
for(d1.move(0); !d1.end(); d1.next())
{
cout << d1.current() << " ";
}
cout << endl;
for(d1.move(d1.length()-1); !d1.end(); d1.pre()) //逆序访问 O(1)
{
cout << d1.current() << " ";
}
cout << endl;
d1.move(d1.length()-1);
while( !d1.end() )
{
if(d1.current() == 5)
{
cout << d1.current() << " ";
d1.remove(d1.find(d1.current()));
}
else
{
d1.pre();
}
}
cout << endl;
for(d1.move(d1.length()-1); !d1.end(); d1.pre())
{
cout << d1.current() << " ";
}
cout << endl;
return 0;
}
6、双向循环链表的实现
目标
使用Linux内核链表实现DTLib中的双向循环链表
Linux内核链表是带头结点的双向循环链表
DualCircleList使用Linux内核链表进行内部实现
DTLib中双向循环链表的设计思路
数据结点之间在逻辑上构成双向循环链表,头结点仅用于结点的定位。
实现思路
-通过模板定义DualCircleList类,继承自DualLinkList类
-在DualCircleList内部使用Linux内核链表进行实现
-使用struct list_head定义DualCircleList的头结点
-特殊处理:循环遍历时忽略头结点
实现要点
-通过list_head进行目标结点定位( position(i) )
-通过list_entry将list_head指针转换为目标结点指针
-通过list_for_each实现int find(const T& e)函数
-遍历函数中的 next() 和 pre() 需要考虑跳过头结点
7、编程实验
基于Linux内核链表的双向循环链表 DualCircleList.h
#ifndef DUALCIRCLELIST_H
#define DUALCIRCLELIST_H
#include "LinuxList.h"
#include "DualLinkList.h"
namespace DTLib
{
template <typename T>
class DualCircleList : public DualLinkList<T>
{
protected:
struct Node : public Object
{
list_head head;
T value;
};
list_head m_header;
list_head* m_current;
list_head* position(int i) const
{
list_head* ret = const_cast<list_head*>(&m_header);
for(int p = 0; p < i; p++)
{
ret = ret->next;
}
return ret;
}
int mod(int i) const
{
return (this->m_length == 0) ? 0 : (i % this->m_length);
}
public:
DualCircleList()
{
this->m_length = 0;
this->m_step = 1;
m_current = NULL;
INIT_LIST_HEAD(&m_header); //初始化为双向循环链表
}
bool insert(const T& e)
{
return insert(this->m_length, e);
}
bool insert(int i, const T& e)
{
bool ret = true;
Node* node = new Node();
i = i % (this->m_length + 1);
if( node )
{
node->value = e;
list_add_tail(&node->head, position(i)->next);
this->m_length++;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create a new element ...");
}
return ret;
}
bool remove(int i)
{
bool ret = true;
i = mod(i);
ret = (0 <= i) && (i < this->m_length);
if( ret )
{
list_head* toDel = position(i)->next;
if(m_current == toDel)
{
m_current = toDel->next;
}
list_del(toDel);
this->m_length--;
delete list_entry(toDel, Node, head);
}
return ret;
}
bool set(int i, const T& e)
{
bool ret = true;
i = mod(i);
ret = (0 <= i) && (i < this->m_length);
if( ret )
{
list_entry(position(i)->next, Node, head)->value = e;
}
return ret;
}
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 = true;
i = mod(i);
ret = (0 <= i) && (i < this->m_length);
if( ret )
{
e = list_entry(position(i)->next, Node, head)->value;
}
return ret;
}
int find(const T& e) const
{
int ret = -1;
int i = 0;
list_head* slider = NULL;
list_for_each(slider, &m_header)
{
if(list_entry(slider, Node, head)->value == e)
{
ret = i;
break;
}
i++;
}
return ret;
}
int length() const
{
return this->m_length;
}
void clear()
{
while(this->m_length > 0)
{
remove(0);
}
}
bool move(int i, int step = 1)
{
bool ret = (step > 0);
i = mod(i);
ret = ret && (0 <= i) && (i < this->m_length);
if( ret )
{
m_current = position(i)->next;
this->m_step = step;
}
return ret;
}
bool end()
{
return ( m_current == NULL) || (this->m_length == 0);
}
T current()
{
if( !end() )
{
return list_entry(m_current, Node, head)->value;
}
else
{
THROW_EXCEPTION(InvalidParameterException, "No value at current position ...");
}
}
bool next()
{
int i = 0;
while((i < this->m_step) && !end())
{
if(m_current != &m_header)
{
m_current = m_current->next;
i++;
}
else
{
m_current = m_current->next; // 遍历时跳过头结点
}
}
if(m_current == &m_header)
{
m_current = m_current->next; // 遍历时跳过头结点
}
return (i == this->m_step);
}
bool pre()
{
int i = 0;
while((i < this->m_step) && !end())
{
if(m_current != &m_header)
{
m_current = m_current->prev;
i++;
}
else
{
m_current = m_current->prev;
}
}
if(m_current == &m_header)
{
m_current = m_current->prev;
}
return (i == this->m_step);
}
~DualCircleList()
{
clear();
}
};
}
#endif // DUALCIRCLELIST_H
main.cpp
#include <iostream>
#include "DualCircleList.h"
using namespace std;
using namespace DTLib;
int main()
{
DualCircleList<int> d1;
for(int i = 0; i < 5; i++)
{
d1.insert(0, i);
d1.insert(0, 5);
}
for(int i = 0; i < d1.length(); i++)
{
cout << d1.get(i) << " ";
}
cout << endl;
d1.move(d1.length() - 1);
while(d1.find(5) != -1)
{
if(d1.current() == 5)
{
cout << d1.current() << " ";
d1.remove(d1.find(d1.current()));
}
else
{
d1.pre();
}
}
cout << endl;
for(int i = 0; i < 10; i++)
{
cout << d1.get(i) << " ";
}
cout << endl;
int i =0;
for(d1.move(d1.length()-1); (i < d1.length()) && (!d1.end()); d1.pre(), i++)
{
cout << d1.current() << " ";
}
return 0;
}
思考题:下面代码中的pn1和pn2是否相等?为什么?
#include <iostream>
#include "LinuxList.h"
#include "Object.h"
using namespace DTLib;
using namespace std;
struct Node : public Object
{
list_head head;
int value;
};
int main()
{
Node node;
list_head* ld = &node.head;
Node* pn1 = reinterpret_cast<Node*>(ld);
Node* pn2 = list_entry(ld, Node, head);
cout << pn1 << endl;
cout << pn2 << endl;
cout << ld << endl;
cout << &node << endl;
return 0;
}
答:reinterpret_cast有“重新解释”之意,起点在结构体第一个成员
宏list_entry本质为container_of,就是通过结构变量中一个成员的地址找到这个结构体变量的首地址。