您的位置:首页 > 理论基础 > 数据结构算法

基础数据结构之数组与链表(五)

2016-02-15 16:33 706 查看
本篇主要介绍一种重要的链表——“双向链表”(Doubly Linked Lists)。

双向链表,顾名思义,它既可以从head开始正向遍历链表,也可以从tail开始反向遍历链表。双向链表的节点包含两个指针,一个指向下一个节点(successor,同单链表),另一个指向前一个节点(predecessor),因此,也可以将它看作是两个迭代方向不同的叠在一起的单链表。如下图a。



以上是几种双向链表的结构图,包括空链表的状态。

其中,图a是最简单的一种双向链表,它包含两个指针,head指向第一个元素,tail指向最后一个元素,完全可以看作是两个走向不同的单链表的叠加。

图b引入了sentinel节点(不是指针)。它是一种编程技巧,前面在介绍单链表的时候已经展示了sentinel节点的作用,无需特殊处理head。

图c是一个可双向循环的循环链表。

图d是带sentinel节点的循环链表。

下面选取图a的结构实现一个双向链表。

#ifndef DOUBLY_LINKED_LIST_H
#define DOUBLY_LINKED_LIST_H

#include <stdexcept>

namespace FoundationalDataStructure
{
// forward declaration
template <typename T>
class DoublyLinkedList;

template <typename T>
class Node
{
public:
T const & Datum() const;
Node const * Prev() const;
Node const * Next() const;

friend DoublyLinkedList<T>;

private:
T datum;
Node * prev;
Node * next;

Node(T const &, Node *, Node *);
};

template <typename T>
class DoublyLinkedList
{
public:
DoublyLinkedList();
~DoublyLinkedList();

DoublyLinkedList(DoublyLinkedList const &);
DoublyLinkedList & operator=(DoublyLinkedList const &);

Node<T> const * Head() const;
Node<T> const * Tail() const;

bool IsEmpty() const;
operator bool() const;    // operator bool() const { return !IsEmpty(); }
T const & First() const;
T const & Last() const;

void Prepend(T const &);
void Append(T const &);
void Extract(T const &);
void Purge();
void InsertAfter(Node<T> const *, T const &);
void InsertBefore(Node<T> const *, T const &);
private:
Node<T> * head;
Node<T> * tail;
};

//////////////////////////////////////////////////Implementation/////////////////////////////////////////////////////////////////////
template <typename T>
Node<T>::Node(T const & _datum, Node * _prev, Node * _next)
: datum(_datum)
, prev(_prev)
, next(_next)
{}

template <typename T>
T const & Node<T>::Datum() const
{
return datum;
}

template <typename T>
Node<T> const * Node<T>::Prev() const
{
return prev;
}
template <typename T>
Node<T> const * Node<T>::Next() const
{
return next;
}

template <typename T>
DoublyLinkedList<T>::DoublyLinkedList()
: head(NULL)
, tail(NULL)
{}

template <typename T>
void DoublyLinkedList<T>::Purge()
{
// The main loop of the Purge function simply traverses all the elements of linked list, deleting each of them one-by-one.
while (head)
{
Node<T> * temp(head);
head = head->next;    // move head pointer
delete temp;
temp = NULL;
}
tail = NULL;
}

template <typename T>
DoublyLinkedList<T>::~DoublyLinkedList()
{
Purge();
}

template <typename T>
Node<T> const * DoublyLinkedList<T>::Head() const
{
return head;
}

template <typename T>
Node<T> const * DoublyLinkedList<T>::Tail() const
{
return tail;
}

template <typename T>
bool DoublyLinkedList<T>::IsEmpty() const
{
return (!head || !tail);
}

template <typename T>
DoublyLinkedList<T>::operator bool() const
{
return !IsEmpty();
}

template <typename T>
T const & DoublyLinkedList<T>::First() const
{
if (!head)
throw std::domain_error("List is empty");

return head->datum;
}

template <typename T>
T const & DoublyLinkedList<T>::Last() const
{
if (!tail)
throw std::domain_error("List is empty");

return tail->datum;
}

template <typename T>
void DoublyLinkedList<T>::Prepend(T const & item)
{
Node<T> * const temp = new Node<T>(item, NULL, head);
if (!head)
tail = temp;    // list is empty
else
head->prev = temp;

head = temp;    // move head to the front of the new list
}

template <typename T>
void DoublyLinkedList<T>::Append(T const & item)
{
Node<T> * const temp = new Node<T>(item, tail, NULL);
if (!tail)
head = temp;    // list is empty
else
tail->next = temp;    // insert temp at the end of the list

tail = temp;    // move tail to the end of the new list
}

template <typename T>
void DoublyLinkedList<T>::Extract(T const & item)    // pass by reference or value ?
{
if (!head)
throw std::domain_error("list is empty");

auto ptr = head;
while (ptr != NULL)
{
if (ptr->datum == item)
{
auto temp = ptr;    // backup

if (ptr == head)
{
if (ptr == tail)
tail = NULL;
else
ptr->next->prev = NULL;

ptr = ptr->next;
head = ptr;
}
else
{
if (ptr == tail)
tail = ptr->prev;
else
ptr->next->prev = ptr->prev;     // bridget over ptr

ptr->prev->next = ptr->next;    // bridget over ptr
ptr = ptr->next;                // move ptr
}

delete temp;
temp = NULL;
continue;    // break; for the first one
}
ptr = ptr->next;
}
}

template <typename T>
DoublyLinkedList<T>::DoublyLinkedList(DoublyLinkedList const & linkedList)
: head(NULL)
, tail(NULL)
{
for (auto ptr = linkedList.Head(); ptr != NULL; ptr = ptr->next)
Append(ptr->datum);
}

template <typename T>
DoublyLinkedList<T> & DoublyLinkedList<T>::operator=(DoublyLinkedList const & linkedList)
{
if (&linkedList != this)
{
Purge();
for (auto ptr = linkedList.Head(); ptr != NULL; ptr = ptr->next)
Append(ptr->datum);
}
return *this;
}

template <typename T>
void DoublyLinkedList<T>::InsertAfter(Node<T> const * arg, T const & item)
{
Node<T> * ptr = const_cast<Node<T>*> (arg);
if (!ptr)
throw std::invalid_argument("invalid position");

Node<T> * const temp = new Node<T>(item, ptr, ptr->next);
if (ptr->next)
ptr->next->prev = temp;

ptr->next = temp;
if (tail == ptr)
tail = temp;
}

template <typename T>
void DoublyLinkedList<T>::InsertBefore(Node<T> const * arg, T const & item)
{
Node<T> * ptr = const_cast<Node<T>*> (arg);
if (!ptr)
throw std::invalid_argument("invalid position");

Node<T> * const temp = new Node<T>(item, ptr->prev, ptr);
if (ptr->prev)
ptr->prev->next = temp;

ptr->prev = temp;
if (head == ptr)
head = temp;
}
} // namespace FoundationalDataStructure

#endif    // DOUBLY_LINKED_LIST_H


注:

1,本文的节点与链表采用的是友元类的实现方式,也可以使用嵌套类的实现方式。

2,函数“operator bool() const; // operator bool() const { return !IsEmpty(); }”的声明和定义格式,以及类模版前缀的添加方式。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: