双向线性链表中每个节点有一个数据域和两个指针,一个指向前向节点,另一个指向后向节点,第一个节点的前指针为空指针,最后一个节点的后指针为空指针。
双向链表的主要优点是对于任意给定的结点,可以很容易地获取其前结点和后结点,主要缺点是每个结点都需要保存prev和next两属性,因此需要更多的空间开销,同时结点的插入与删除操作也会变得更加耗时,需要更多指向操作。
见如下程序,构建一个双向线性链表及其相关函数:
#include <cstring>
#include <iostream>
#include <stdexcept>
using namespace std;
// 双向线性链表
template<typename T>
class List { //做成一个类模板
public:
// 构造、析构、拷贝构造、拷贝赋值
List(void) : m_head(NULL), m_tail(NULL) {}
//一开始构造时为空指针,因为指针成员如果没有初始化的话就会变成野指针
~List(void) {
clear(); //把元素全部删掉
}
List(List const& that) : //实现一个支持深拷贝的拷贝构造函数
m_head(NULL), m_tail(NULL) {
for (Node* node = that.m_head;
node; node = node->m_next)
push_back(node->m_data);
}
List& operator= (List const& rhs) { //拷贝赋值函数
if (rhs != this) {
List list = rhs;
swap(m_head, list.m_head); //交换,当调用析构函数后,既拿到了新的副本,又清除了原有的资源
swap(m_tail, list.m_tail);
}
return *this;
}
// 获取首元素的引用
T& front(void) {
if (empty())
throw underflow_error(
"链表下溢!");
return m_head->m_data;
}
T const& front(void) const { //常版本
return const_cast<List*> (
this)->front();
}
// 向首部压入
void push_front(T const& data) {
m_head = new Node(data, NULL,
m_head);
if (m_head->m_next)
m_head->m_next->m_prev =
m_head;
else
m_tail = m_head;
}
// 从首部弹出
void pop_front(void) {
if (empty())
throw underflow_error(
"链表下溢!");
Node* next = m_head->m_next;
delete m_head;
m_head = next;
if (m_head)
m_head->m_prev = NULL;
else
m_tail = NULL;
}
// 获取尾元素
T& back(void) {
if (empty())
throw underflow_error(
"链表下溢!");
return m_tail->m_data;
}
T const& back(void) const {
return const_cast<List*> (
this)->back();
}
// 向尾部压入
void push_back(T const& data) {
m_tail = new Node(data, m_tail);
if (m_tail->m_prev)
m_tail->m_prev->m_next =
m_tail;
else
m_head = m_tail;
}
// 从尾部弹出
void pop_back(void) {
if (empty())
throw underflow_error(
"链表下溢!");
Node* prev = m_tail->m_prev;
delete m_tail;
m_tail = prev;
if (m_tail)
m_tail->m_next = NULL;
else
m_head = NULL;
}
// 删除所有匹配元素
void remove(T const& data) {
for (Node* node = m_head, *next; node; node = next) {
next = node->m_next;
if (node->m_data == data) {
if (node->m_prev)
node->m_prev->m_next = node->m_next;
else
m_head = node->m_next;
if (node->m_next)
node->m_next->m_prev = node->m_prev;
else
m_tail = node->m_prev;
delete node;
}
}
}
// 清空
void clear(void) {
while (!empty())
pop_back();
}
// 判空
bool empty(void) const {
return !m_head && !m_tail;
}
// 大小
size_t size(void) const {
size_t nodes = 0;
for (Node* node = m_head; node; node = node->m_next)
++nodes;
return nodes;
}
// 插入输出流
friend ostream& operator<< (
ostream& os, List const& list) {
for (Node* node = list.m_head; node; node = node->m_next)
os << *node;
return os;
}
private:
// 节点
class Node {
public:
Node(T const& data, Node* prev = NULL,Node* next = NULL) :
m_data(data), m_prev(prev),m_next(next) {}
friend ostream& operator<< (ostream& os,Node const& node) { //输出
return os << '(' << node.m_data << ')';
}
T m_data; // 数据
Node* m_prev; // 前指针
Node* m_next; // 后指针
};
Node* m_head; // 头指针
Node* m_tail; // 尾指针
};
// 测试用例
void test(void) {
List<int> li;
li.push_front(30);
li.push_front(20);
li.push_front(10);
li.push_front(0);
cout << li << endl;
++li.front();
cout << li << endl;
li.pop_front();
cout << li << endl;
li.push_back(60);
cout << li << endl;
li.back()++;
cout << li << endl;
li.pop_back();
cout << li << endl;
li.remove(30);
cout << li << endl;
}
// 进程入口
int main(void) {
test();
return 0;
}
执行结果如下:
(0)(10)(20)(30)
(1)(10)(20)(30)
(10)(20)(30)
(10)(20)(30)(60)
(10)(20)(30)(61)
(10)(20)(30)
(10)(20)