最近在维护项目时发现了项目中有这样一个功能需求:程序需要发送许多指令到下位机,在这些指令中,有的指令需要循环发送到下位机,有的指令则需要根据需求进行发送。如果我们要使用某个容器来管理这些指令,对于那些临时发送的指令这个容器需要满足动态插入和删除的功能,程序使用双向循环链表满足了这个需求,在这里作为一个知识点记录一下,然后把代码里面的双向循环链表模板类的代码整理出来,进行一下说明。
首先,我们说明一下什么是双向循环链表,双向链表又称为双链表,在双向链表的每个节点中有两个链接指针作为它的数据成员,pred指向其前驱节点,next指向其后继节点。再加上数据域,因此每个双向链表至少包括三个域。
使用双向链表的目的是为了解决在链表中直接访问前驱和后继的问题。因为它可以直接访问前驱和后继节点,所以,我们可以非常方便的在链表中动态的添加删除元素。这样也就满足了我们项目的功能需求了,下面就是双向循环链表的示意图。
下面我们代码实现一个双向循环链表的模板类,首先是节点类,成员就是一个前驱指针,一个后继指针,加上一个数据域。
/*
================================================
Name : dclinkedlist.h
Author :
Copyright :
Description :包含一个双向链表模板类的声明和实现
==================================================
*/
#ifndef DOUBLY_LINKED_NODE_CLASS
#define DOUBLY_LINKED_NODE_CLASS
#include <stdlib.h>
template <class T>
class DNode
{
private:
DNode<T> *pred,*next;//指向前、后结点的指针
public:
T data;//data 数据域
// 构造函数
DNode(void); // 创建空表并保留数据未定义,用于创建链表的头
DNode (const T& item); // 建立一个指向自身的结点并初始化data域,用于创建链表头以外其他节点
void InsertNext(DNode<T> *p);// 将结点p插入到双向链表中当前结点的前面
void InsertPred(DNode<T> *p);// 将结点p插入到双向链表中当前结点的后面
DNode<T> *DeleteNode(void); // 从链表中删除当前结点并返回其地址
DNode<T> *NextNode(void) const;// 取得指向前面结点的指针
DNode<T> *PredNode(void) const;// 取得指向后面结点的指针
};
template <class T>
DNode<T>::DNode(void)
{
pred = next = this;
}
template <class T>
DNode<T>::DNode(const T& item)
{
pred = next = this;
data = item;
}
template <class T>
void DNode<T>::InsertNext(DNode<T> *p)
{
p->next = next;
next->pred = p;
p->pred = this;
next = p;
}
template <class T>
void DNode<T>::InsertPred(DNode<T> *p)
{
p->pred = pred;
pred->next = p;
p->next = this;
pred = p;
}
template <class T>
DNode<T> *DNode<T>::DeleteNode(void)
{
pred->next = next;
next->pred = pred;
return this;
}
template <class T>
DNode<T> *DNode<T>::NextNode(void) const
{
return next;
}
template <class T>
DNode<T> *DNode<T>::PredNode(void) const
{
return pred;
}
#endif // DOUBLY_LINKED_NODE_CLASS
下面是链表类,它满足了节点插入,删除,遍历等所有可能用到的操作。总的来说还是比较方便使用的。这里要注意的是size成员表示链表中节点个数,但是这个数量是不计算头节点的,因为头结点数据域我们并没有用来存放数据,如果size是3的话,其实加上头结点这个链表的节点数实际是4。
/*
================================================
Name : dclinkedlist.h
Author :
Copyright :
Description :包含一个双向链表模板类的声明和实现
==================================================
*/
#ifndef DOUBLE_CIRCULAR_LINKEDLIST_CLASS
#define DOUBLE_CIRCULAR_LINKEDLIST_CLASS
#include <stdlib.h>
#include "dnode.h"
/**
*
*
*/
template <class T>
class DCLinkedList
{
private:
DNode<T> *header, *currPtr;// 指向表头的指针和当前结点的指针
int size; // 表中的元素个数,但不包括表头节点
void CopyList(const DCLinkedList<T> &L); // 将表L拷贝到当前表尾
public:
// 构造函数
DCLinkedList(void);
DCLinkedList(const DCLinkedList<T> &L);
// 析构函数
~DCLinkedList(void);
// 赋值运算符
DCLinkedList<T>& operator= (const DCLinkedList<T> &L);
//检查表状态的函数
int ListSize(void) const;
bool ListEmpty(void) const;
// 遍历表的函数
void Reset(bool bheader = true);// 是从表头开始遍历,还是表尾(反向遍历)
void Next(void);
void Prev(void);
bool EndOfList(void) const;
// 插入函数
void InsertFront(const T &item);
void InsertRear(const T &item);
void InsertAt(const T &item);
void InsertAfter(const T &item);
// 删除函数
void DeleteFront(void);
void DeleteRear(void);
void DeleteAt(void);
T& Data(void);// 返回当前节点的数据
bool Find(const T& item);//判断链表中是否包含该节点
void ClearList(void);// 清空表的函数
bool HeadECurIf(void);// 判断当前是否是头
};
template <class T>
DCLinkedList<T>::DCLinkedList(void):size(0)
{
currPtr = header = new DNode<T>();
}
template <class T>
DCLinkedList<T>::DCLinkedList(const DCLinkedList<T>& L):size(0)
{
currPtr = header = new DNode<T>();
CopyList(L);
}
template <class T>
DCLinkedList<T>::~DCLinkedList(void)
{
ClearList();
delete header;
}
template <class T>
void DCLinkedList<T>::CopyList(const DCLinkedList<T> &L)
{
DNode<T> *p = L.header->NextNode();
while (p != L.header)
{
InsertRear(p->data);
p = p->NextNode();
}
}
template <class T>
int DCLinkedList<T>::ListSize(void) const
{
return size;
}
template <class T>
bool DCLinkedList<T>::ListEmpty(void) const
{
return (size == 0);
}
template <class T>
void DCLinkedList<T>::Reset(bool bheader)
{
if (bheader)
{
currPtr = header->NextNode(); // 表头
}
else
{
currPtr = header->PredNode(); // 表尾
}
}
template <class T>
void DCLinkedList<T>::Next(void)
{
currPtr = currPtr->NextNode();
}
template <class T>
void DCLinkedList<T>::Prev(void)
{
currPtr = currPtr->PredNode();
}
template <class T>
bool DCLinkedList<T>::EndOfList(void) const
{
return (currPtr == header);
}
template <class T>
void DCLinkedList<T>::InsertFront(const T &item)
{
Reset();
InsertAt(item);
}
template <class T>
void DCLinkedList<T>::InsertRear(const T &item)
{
currPtr = header;
InsertAt(item);
}
template <class T>
void DCLinkedList<T>::InsertAt(const T &item)
{
DNode<T> *newNode = new DNode<T>(item);
currPtr->InsertPred(newNode);
currPtr = newNode;
size++;
}
template <class T>
void DCLinkedList<T>::InsertAfter(const T &item)
{
DNode<T> *newNode = new DNode<T>(item);
currPtr->InsertPred(newNode);
size++;
}
template <class T>
void DCLinkedList<T>::DeleteFront(void)
{
Reset();
DeleteAt();
}
template <class T>
void DCLinkedList<T>::DeleteRear(void)
{
Reset(false);
DeleteAt();
}
template <class T>
void DCLinkedList<T>::DeleteAt(void)
{
if (currPtr == header)
{
return;
}
DNode<T> *p = currPtr->NextNode();
delete (currPtr->DeleteNode());
currPtr = p;
size --;
}
template <class T>
T& DCLinkedList<T>::Data(void)
{
return currPtr->data;
}
template <class T>
bool DCLinkedList<T>::Find(const T& item)
{
for (Reset(); !EndOfList(); Next())
{
if (Data() == item)
{
return true;
}
}
return false;
}
template <class T>
DCLinkedList<T>& DCLinkedList<T>::operator= (const DCLinkedList<T>& L)
{
if (this == &L)
{
return *this;
}
ClearList();
CopyList(L);
return *this;
}
template <class T>
void DCLinkedList<T>::ClearList(void)
{
Reset();
while (currPtr != header)
{
DeleteAt();
}
}
template <class T>
bool DCLinkedList<T>::HeadECurIf(void)
{
if (currPtr == header)
{
return true;
}
else
{
return false;
}
}
#endif // DOUBLE_CIRCULAR_LINKEDLIST_CLASS