数据结构(面向对象方法与C++语言描述)(第2版)双向循环链表内容整理
双向循环链表
双向链表又称双链表。使用双向链表(doubly linked list)的目的是为了解决在链表中访问直接前驱和直接后继的问题。因为在双向链表中每个节点都有两个链指针,一个指向节点的直接前驱,一个指向节点的直接后继,这样不论是向前驱方向搜索还是向后继方向搜索,其时间开销都只有O(1)。
在双向链表的每个结点中应有两个链接指针作为它的数据成员:lLink指示它的前驱节点,rLink指示它的后继结点。因此,双向链表的每个结点至少有3个域:
lLink又称为左链指针,rLink又称为右链指针。双向链表常采用附加头结点的循环链表方式。一个双向链表有一个附加头结点,由链表的头指针first指示,它的data域或者不存放数据,或者存放一个特殊要求的数据;它的lLink指向双向链表的尾指针(最后一个节点),它的rLink指向双向链表的首元结点(第一个结点)。链表的首元结点的左链指针lLink和尾指针的右链指针rLink都指向附加头结点。
假设指针 p 指向双向循环链表的某一结点,那么,p->lLink 指示 p 所指结点的前驱结点,p->lLink->rLink 中存放的是 p 所指结点的前驱结点的后继结点的地址,即 p 所指结点本身;同样地,p->rLink指示 p 所指结点的后继结点,p->rLink->lLink 也指向 p所指结点本身。因此有 p == p->lLink->rLink == p->rLink->lLink,见图。
代码实现
环境:vs2019
头文件:DblList.h
源文件:main.cpp
DblList.h代码:
#pragma once
#include <iostream>
template<typename T>
struct DblNode {
T data; //链表结点数据
DblNode<T>* lLink, * rLink; //链表前驱(左链)、后继(右链)指针
DblNode(T value = 0, DblNode<T>* left = nullptr, DblNode<T>* right = nullptr)
:data(value), lLink(left), rLink(right) {} //构造函数
};
template<typename T>
class DblList {
public:
DblList(T uniqueVal); //构造函数:建立附加头结点
DblList(DblList<T>& L); //拷贝构造
~DblList(); //析构函数:释放所用存储
void makeEmpty(); //将链表置空
int Length()const; //计算双链表的长度
bool IsEmpty() { return first->rlink == first; } //判双链表空否
DblNode<T>* getHead()const { return first; } //取附加头结点地址
void setHead(DblNode<T>* ptr) { first = ptr; } //设置附加头结点地址
DblNode<T>* Search(const T& x); //在链表中沿后继方向寻找等于给定值x的结点
DblNode<T>* Locate(int i, int d); //在链表中定位序号为i(>=0)的结点,d=0按前驱方向,d≠0按后继方向
bool Insert(int i, const T& x, int d); //在第i个结点后插入一个包含值x的新结点,d=0按前驱方向,d≠0按后继方向
bool Remove(int i, T& x, int d); //删除第i个结点,x返回其值,d=0按前驱方向,d≠0按后继方向
void output(int d); //输出,d=0按前驱方向,d≠0按后继方向
private:
DblNode<T>* first;
};
template<typename T>
inline DblList<T>::DblList(T uniqueVal)
{
//构造函数:建立双向循环链表的附加头结点,它包含了一个用于某些特定情况的值。
first = new DblNode<T>(uniqueVal);
if (first == nullptr)
{
std::cerr << "存储分配出错!" << std::endl;
exit(1);
}
first->rLink = first->lLink = first;
}
template<typename T>
inline DblList<T>::DblList(DblList<T>& L)
{
//拷贝构造函数
T value;
DblNode<T>* srcptr = L.getHead();
DblNode<T>* destptr = first = new DblNode<T>;
while (srcptr->rLink != L.first) //逐个结点复制
{
value = srcptr->rLink->data;
destptr->rLink = new DblNode<T>(value);
destptr->rLink->lLink = destptr;
destptr = destptr->rLink;
srcptr = srcptr->rLink;
}
destptr->rLink = first;
first->lLink = destptr;
}
template<typename T>
inline DblList<T>::~DblList()
{
makeEmpty();
if (first != nullptr)
{
delete first;
first = nullptr;
}
}
template<typename T>
inline void DblList<T>::makeEmpty()
{
//将链表置位空表
DblNode<T>* q;
while (first->rLink != first)
{
q = first->rLink;
first->rLink = q->rLink;
delete q;
}
first->lLink = first;
first->rLink = first;
}
template<typename T>
inline int DblList<T>::Length() const
{
//计算带附加头接地那的双向循环链表的长度,通过函数返回。
DblNode<T>* current = first->rLink;
int count = 0;
while (current != first)
{
current = current->rLink;
count++;
}
return count;
}
template<typename T>
inline DblNode<T>* DblList<T>::Search(const T& x)
{
//在带附加头结点的双向循环链表中寻找其值等于x的节点,若找到,则函数返回该结点地址,
//否则函数返回nullptr
DblNode<T>* current = first->rLink;
while (current != first && current->data != x)
{
current = current->rLink;
}
if (current != first) return current; //搜索成功
else return nullptr;
}
template<typename T>
inline DblNode<T>* DblList<T>::Locate(int i, int d)
{
//在带附加头结点的双向循环链表中按d所指方向寻找第i个结点的地址。若d=0,在前驱方向
//寻找第i个结点,若d≠0,在后继方向寻找第i个结点
if (first->rLink == first || i == 0) return first;
DblNode<T>* current;
if (d == 0) current = first->lLink; //搜索方向
else current = first->rLink;
for (int j = 0; j < i; j++) //逐个结点检测
{
if (current == first) break; //链太短退出搜索
else if (d == 0) current = current->lLink;
else current = current->rLink;
}
if (current != first) return current; //搜索成功
else return nullptr; //搜索失败
}
template<typename T>
inline bool DblList<T>::Insert(int i, const T& x, int d)
{
//建立一个包含有值x的新结点,并将其按d指定的方向插入到第i个结点之后。
DblNode<T>* current = Locate(i, d); //查找第i个结点
if (current == nullptr) return false; //i不合理,插入失败
DblNode<T>* newNode = new DblNode<T>(x);
if (newNode == nullptr)
{
std::cerr << "存储分配失败!" << std::endl;
exit(1);
}
if (d == 0) //前驱方向插入
{
newNode->lLink = current->lLink;
current->lLink = newNode;
newNode->lLink->rLink = newNode;
newNode->rLink = current;
}
else //后继方向插入
{
newNode->rLink = current->rLink;
current->rLink = newNode;
newNode->rLink->lLink = newNode;
newNode->lLink = current;
}
return true;
}
template<typename T>
inline bool DblList<T>::Remove(int i, T& x, int d)
{
//在带附加头结点的双向循环链表中按照d所指方向删除第i个结点
DblNode<T>* current = Locate(i, d); //查找第i个结点
if (current == nullptr) return false; //i不合理,删除失败
current->rLink->lLink = current->lLink; //从lLink链中摘下
current->lLink->rLink = current->rLink; //从rLink链中摘下
x = current->data;
delete current; //删除
return true; //删除成功
}
template<typename T>
inline void DblList<T>::output(int d)
{
//双向循环链表的输出函数:将循环链表中按d指定的方向输出到屏幕上。
//若d=0,从头结点向前输出,若d≠0,从头结点向后输出。
std::cout << "链表中的元素为: ";
DblNode<T>* current;
if (d == 0) current = first->lLink;
else current = first->rLink;
while (current != first)
{
std::cout << current->data << " ";
if (d == 0) current = current->lLink;
else current = current->rLink;
}
std::cout << std::endl;
}
main.cpp代码:
#include "DblList.h"
int main()
{
DblList<int> L1(0);
//后插法:在头结点前驱结点插入
L1.Insert(0, 10, 0);
L1.Insert(0, 20, 0);
L1.Insert(0, 30, 0);
std::cout << "逆向输出链表元素" << std::endl;
L1.output(0);
std::cout << "正向输出链表元素" << std::endl;
L1.output(1);
L1.makeEmpty();
//前插法:在头结点后继结点插入
L1.Insert(0, 11, 1);
L1.Insert(0, 22, 1);
L1.Insert(0, 33, 1);
std::cout << "逆向输出链表元素" << std::endl;
L1.output(0);
std::cout << "正向输出链表元素" << std::endl;
L1.output(1);
DblList<int> L2(L1);
std::cout << "逆向输出链表元素" << std::endl;
L1.output(0);
std::cout << "正向输出链表元素" << std::endl;
L1.output(1);
return 0;
}
控制台界面: