在设计链表增删操作的方法时,要注意增删位置在链表中的位置,就只有三种情况,head位置,tail位置和中间位置,不同位置对指针的连接方式会有不同,一般都要用if进行判断,这些在代码中已经解释的很详细了。除此之外,在首尾进行操作时,也要对节点数量进行判断,也有三种情况,最简单的情形是空链表,其次是单节点,再就是一般的多节点情形,针对不同的情形用if进行判断,并做好指针维护即可,这些在代码中也有详细的注释。
#include<iostream>
using namespace std;
//DLLNode**************************************************************************************
template<typename T>
class DLLNode
{
public:
DLLNode();
DLLNode(T el, DLLNode* p=nullptr, DLLNode* n=nullptr);//重载构造函数,函数声明是可以带默认参数的
~DLLNode();
T info;//数据块,模板数据类型
DLLNode* prev, * next;//指向前后方向的两个指针
private:
};
template<typename T>
DLLNode<T>::DLLNode()//默认构造函数
{
T = nullptr;
next = prev = nullptr;
}
template<typename T>
DLLNode<T>::~DLLNode()
{
}
template<typename T>
DLLNode<T>::DLLNode(T el, DLLNode* p , DLLNode* n )
{
this->info = el;
this->next = n;
this->prev = p;
}
//DoubleLinkedList*****************************************************************************
template<typename T>
class DoubleLinkedList
{
public:
DoubleLinkedList();
~DoubleLinkedList();
//与一般链表一样的操作集合
int isEmpty()
{
return head == nullptr;
}
void addToHead(T el)//在首尾添加节点基本都要进行三种类型的判断,空,唯一节点和多节点
{
if (head == nullptr)
{
DLLNode<T>* node = new DLLNode<T>(el);//只传入了一个参数,该节点的前后指针都默认置空
head = tail = node;//唯一节点的情形
}
else if (head!=nullptr)
{
DLLNode<T>* node = new DLLNode<T>(el,nullptr,head);//新head的前指针处传入nullptr,后指针接原来的head节点
head->prev = node;//注意双向链表的prev指针的维护,不能漏掉!旧head的前指针指向node(以前是指向nullptr的),后指针不管
head = node;//新head指向新添加的node
}
}
void addToTail(T el)
{
DLLNode<T>* node = new DLLNode<T>(el, tail, nullptr);//新节点的前指针指向tail,后指针指向nullptr
//其实先不用进行tail的nullptr判断,如果tail为空,则传入的tail也是nullptr的,因此新节点就是唯一节点,且前后指针为nullptr
if (tail == nullptr)//证明传入新node前list是空的,这时要把head和tail指针都指向新的唯一的node
{
head = tail = node;
}
else//tail不空,则需要维护旧tail的指针,主要是更改next指针(以前是指向nullptr的),使其指向新的node,旧tail的前指针不管
{
tail->next = node;
tail = node;//再将tail后移
}
}
T deleteFromHead()//删除并且返回info,在首尾的操作都要先进行情况判断,然后注意双向的指针维护
{
T el=0;
if (head == nullptr)//空表情形
{
cout << "空表,操作错误!返回-999!" << endl;
return -999;
}
else
{
if (head == tail)//唯一节点情形
{
el = head->info;//取出数据
delete head;//砍头
head = tail = nullptr;//成空表
}
else//多节点情形
{
el = head->info;//取出数据
DLLNode<T>* tmp = head->next;//临时指针tmp,用来保存第二个节点的地址,用于稍后赋值给新head
tmp->prev = nullptr;//新的head的prev指针要置空
delete head;//砍头,注意,head的空间被delete了,但是head指针还是存在,需要维护
head = tmp;//将head指针移动到新头结点上
}
}
return el;//别忘了返回值
}
T deleteFromTail()
{
T el=0;
if (tail == nullptr)//现在应该熟练了,上来先判空
{
cout << "空表,操作错误!返回-999!" << endl;
return -999;
}
else
{
if (head == tail)//再看看节点数量情况,是否是单一节点
{
el = tail->info;//取出数据
delete tail;//去尾!
head = tail = nullptr;//成空表,首尾置空
}
else//多节点情形
{
el = tail->info;//取出数据
DLLNode<T>* tmp = tail->prev;//临时变量找出未来的新尾巴,就是倒数第二个节点
tmp->next = nullptr;//新的tail的next指针要置空
delete tail;//去尾!
tail = tmp;//新tail指向以前的倒数第二个
}
}
return el;
}
void deleteNode(T el)
{
DLLNode<T>* node;//用于标记的node,用于标记目标节点
for (node = head; node->info != el && node != tail; node = node->next);//开始遍历
if (node == tail)//list的tail是目标节点或者list中没有找到目标值el
{
if (node->info == el)//尾节点有目标值
{
deleteFromTail();//直接调用现成的函数
}
cout << "没有目标值,删除失败" << endl;
}
else//找到目标值,node所指的节点就是要删除的节点
{
if (node == head)deleteFromHead();//目标在头节点,直接调用现成的函数
else//目标节点在中间
{
node->prev->next = node->next;
node->next->prev = node->prev;//node前后节点的双向指针维护
delete node;//再见了大兄弟
}
}
}
bool isInList(T el)const
{
DLLNode<T>* node;//用于标记的node,用于标记目标节点
for (node = head; node->info != el && node != tail; node = node->next);//开始遍历
if (node == tail)
{
if (node->info == el)return true;
else
{
return false;
}
}
else
{
return true;
}
}
private:
protected:
DLLNode<T>* head, * tail;//注意类名称的写法
};
template<typename T>
DoubleLinkedList<T>::DoubleLinkedList()//默认构造函数,产生空list
{
head = tail = nullptr;
}
template<typename T>
DoubleLinkedList<T>::~DoubleLinkedList()
{
}