双链表
单链表只允许从一个表结点直接访问它的后继结点,而双链表可以从一个表结点出发,在线性表中随意访问它的前驱结点和后继接电脑。双链表存储两个指针,使这些成为可能:一个智指向它的后继结点(与单链表相同),另一个指向它的前驱结点。如下图:
下面是双链表的结点类实现:
template <class Elem> class Node
{
private:
static Node<Elem>* freelist; // 空闲表
public:
Elem element; // 该结点的值
Node* next; // 指向下一个结点
Node* prev;
// 两个构造函数
Node(const Elem& elemval, Node* preval = nullptr, Node* nextval = nullptr)
{
element = elemval; prev = preval; next = nextval;
}
Node(Node* preval = nullptr, Node* nextval = nullptr) { prev = preval; next = nextval; }
// 重载操作符
void* operator new(size_t);
void operator delete(void*);
};
template <class Elem>
Node<Elem>* Node<Elem>::freelist = nullptr;
template <class Elem>
void* Node<Elem>::operator new(size_t)
{
// 如果空闲表为空,调用系统的new创建一个Node对象
if (freelist == nullptr) return ::new Node;
Node<Elem>* temp = freelist;
freelist = freelist->next;
return temp;
}
template <class Elem>
void Node<Elem>::operator delete(void* ptr)
{
((Node<Elem>*)ptr)->next = freelist;
freelist = (Node<Elem>*)ptr;
}
而对于链表类的实现,只需改变insert,append,remove,prev函数即可:
// 在右表最前面插入元素
template <class Elem>
bool DLList<Elem>::insert(const Elem& item)
{
// 在栅栏后插入新结点,把新结点的next指向栅栏后一个结点,prev指向栅栏,再把本身的地址附给栅栏后面
fence->next = new Node<Elem>(item, fence, fence->next);
// 如果新结点不是最后一个结点,就把后一个结点的prev指向新结点
if (fence->next->next != nullptr)
fence->next->next->prev = fence->next;
if (tail == fence)
tail = fence->next;
++rightcnt;
return true;
}
// 在右表末尾拼接元素
template <class Elem>
bool DLList<Elem>::append(const Elem& item)
{
tail = tail->next = new Node<Elem>(item, tail, nullptr);
++rightcnt;
return true;
}
// 删除右表第一个元素
template <class Elem>
bool DLList<Elem>::remove(Elem& item)
{
// 右表为空
if (fence->next == nullptr)
return false;
// 读出要删除的值
item = fence->next->element;
// 创建临时结点
Node<Elem>* ntemp = fence->next;
// 如果右表只剩下最后一个元素
if (ntemp->next != nullptr)
ntemp->next->prev = fence;
else tail = fence;
fence->next = ntemp->next;
delete ntemp;
--rightcnt;
return true;
}
// 把栅栏的位置向左移动一个单位,如果栅栏已经是表头就不移动
template <class Elem>
void DLList<Elem>::prev()
{
if (fence != head)
{
fence = fence->prev;
--leftcnt;
++rightcnt;
}
}
双链表与单链表相比唯一的缺点就是使用更多的空间需求。双链表的每一个结点需要两个指针。这种实现方法,它需要的结构性开销是单链表的两倍。