列表
头、尾节点
List对象的内部组成及逻辑结构如图所示,其中私有的头节点( header) 和尾节点
( trailer) 始终存在,但对外并不可见。对外部可见的数据节点如果存在,则其中的第一个和最后一个节点分别称作首节点( first node) 和末节点( last node) 。
就内部结构而言, 头节点紧邻于首节点之前, 尾节点紧邻于末节点之后。这类经封装之后从外部不可见的节点, 称作哨兵节点( sentinel node) 。
默认构造方法
创建List对象时,默认构造方法将调用如代码所示的统一初始化过程init(), 在列表
内部创建一对头、尾哨兵节点,并适当地设置其前驱、后继指针构成一个双向链表。
示例程序(初始化过程)
template <typename T> void List<T>::init() {
//列表初始化,在创建列表对象时统一调用
header = new ListNode<T>; //创建头哨兵节点
trailer = new ListNode<T>; //创建尾哨兵节点
header->succ = trailer; header->pred = NULL;
trailer->pred = header; trailer->succ = NULL;
_size = 0; //记录规模
}
该链表对外的有效部分初始为空, 哨兵节点对外不可见, 此后引入的新节点都将陆续插入于这一对哨兵节点之间
由秩到位置的转换
通过重载操作符“[]” ,提供一个转换接口。
template <typename T> //重载下标操作符,以通过秩直接访问列表节点(虽方便,效率低,需慎用)
T& List<T>::operator[] ( Rank r ) const {
//assert: 0 <= r < size
ListNodePosi(T) p = first(); //从首节点出发
while ( 0 < r-- ) p = p->succ; //顺数第r个节点即是
return p->data; //目标节点,返回其中所存元素
}
查找
template <typename T> //在无序列表内节点p(可能是trailer)的n个(真)前驱中,找到等于e的最后者
ListNodePosi(T) List<T>::find ( T const& e, int n, ListNodePosi(T) p ) const {
while ( 0 < n-- ) //(0 <= n <= rank(p) < _size)对于p的最近的n个前驱,从右向左
if ( e == ( p = p->pred )->data ) return p; //逐个比对,直至命中或范围越界
return NULL; //p越出左边界意味着区间内不含e,查找失败
} //失败时,返回NULL
插入
template <typename T> ListNodePosi(T) List<T>::insertAsFirst ( T const& e )
{
_size++; return header->insertAsSucc ( e ); } //e当作首节点插入
template <typename T> ListNodePosi(T) List<T>::insertAsLast ( T const& e )
{
_size++; return trailer->insertAsPred ( e ); } //e当作末节点插入
template <typename T> ListNodePosi(T) List<T>::insertA ( ListNodePosi(T) p, T const& e )
{
_size++; return p->insertAsSucc ( e ); } //e当作p的后继插入(After)
template <typename T> ListNodePosi(T) List<T>::insertB ( ListNodePosi(T) p, T const& e )
{
_size++; return p->insertAsPred ( e ); } //e当作p的前驱插入(Before)
基于复制的构造
copyNode()
template <typename T> //列表内部方法:复制列表中自位置p起的n项
void List<T>::copyNodes ( ListNodePosi(T) p, int n ) {
//p合法,且至少有n-1个真后继节点
init(); //创建头、尾哨兵节点并做初始化
while ( n-- ) {
insertAsLast ( p->data ); p = p->succ; } //将起自p的n项依次作为末节点插入
}
在输入参数合法的前提下,copyNodes()首先调用init()方法, 创建头、尾哨兵节点并做相应的初始化处理,然后自p所指节点起,从原列表中取出n个相邻的节点,并逐一作为末节点插至新列表中。
template <typename T> //复制列表中自位置p起的n项(assert: p为合法位置,且至少有n-1个后继节点)
List<T>::List ( ListNodePosi(T) p, int n ) {
copyNodes ( p, n ); }
template <typename T> //整体复制列表L
List<T>::List ( List<T> const& L ) {
copyNodes ( L.first(), L._size ); }
template <typename T> //复制L中自第r项起的n项(assert: r+n <= L._size)
List<T>::List ( List<T> const& L, int r, int n ) {
ListNodePosi(T) p = L.first();
while ( 0 < r-- ) p = p->succ;
copyNodes ( p, n );
}
删除
在列表中删除指定节点p的算法。
template <typename T> T List<T>::remove ( ListNodePosi(T) p ) {
//删除合法节点p,返回其数值
T e = p->data; //备份待删除节点的数值(假定T类型可直接赋值)
p->pred->succ = p->succ; p->succ->pred = p->pred; //后继、前驱
delete p; _size--; //释放节点,更新规模
return e; //返回备份的数值
}
析构
template <typename T> List<T>::~List() //列表析构器
{
clear(); delete header; delete trailer; } //清空列表,释放头、尾哨兵节点
template <typename T> int List<T>::clear() {
//清空列表
int oldSize = _size;
while ( 0 < _size ) remove ( header->succ ); //反复删除首节点,直至列表变空
return oldSize;
}
唯一化
template <typename T> int List<T>::deduplicate() {
int oldSize = _size; ListNodePosi(T) p = first(); ListNodePosi(T) q = NULL;
for ( Rank r = 0; p != trailer; p = p->succ, q = find ( p->data, r, p ) )
q ? remove ( q ): r++; //r为无重前缀的长度
return oldSize - _size; //即被删除元素总数
}
遍历
template <typename T> void List<T>::traverse ( void ( *visit ) ( T& ) ) //借助函数指针机制遍历
{
for ( ListNodePosi(T) p = header->succ; p != trailer; p = p->succ ) visit ( p->data ); }
template <typename T> template <typename VST> //元素类型、操作器
void List<T>::traverse ( VST& visit ) //借助函数对象机制遍历
{
for ( ListNodePosi(T) p = header->succ; p != trailer; p = p->succ ) visit ( p->data ); }