双向链表,在进行数据的存储时不光存储数据和后继节点,还要有一块区域存储前驱节点。在插入和删除时不需要额外的代码来寻找第i-1个节点,直接使用此节点的前驱节点就可以找到它的上一个节点。具体结构如下图所示。
双向链表的节点
双向链表的节点包含三个部分,分别是数据域,前驱域和后继域。
//双向链表的节点
template <typename datatype>
struct two_way_listnode
{
datatype value;//存储的值
two_way_listnode* prior;//前驱节点
two_way_listnode* next;//后继节点
};
双向链表类
双向链表的私有成员有一个指向头结点的头指针head,成员函数有无参构造函数,有参构造函数(头插或尾插),打印双向链表的函数(遍历),在第i个节点插入的函数,求双向链表的长度的函数,按位序查找的函数,按值查找的函数,判空函数,删除双向链表的节点的函数,析构双向链表的函数。
//双向链表类
template <typename datatype>
class two_way_list
{
public:
two_way_list();//无参构造函数,建立空链表
two_way_list(datatype [],int,int);//有参构造函数,用(数组+头插)的形式来构建链表
two_way_list(datatype[], int);//有参构造函数,用(数组+尾插)的形式来构建链表
void printlist();//打印双向链表
void insert_two_way_list(int,datatype);//双向链表在第i个节点插入(不需要提前找第i个节点)
int length();//求双向链表的长度
datatype get(int);//按位序查找
int locate(datatype);//按值查找
bool empty();//判空函数
void delete_two_way_list(int);//删除双向链表的节点
~two_way_list();//析构双向链表
private:
two_way_listnode<datatype>* head;
};
无参构造函数
创建一个只有头结点的空链表,即将头结点的前驱和后继都指向空。
//无参构造函数,建立空链表
template <typename datatype>
two_way_list<datatype>::two_way_list()
{
head = new two_way_listnode<datatype>;
head->prior = NULL;
head->next = NULL;
}
有参构造函数(数组+头插)
每次从链表的头部进行插入,在进行插入时有四条链的变换。值得注意的是,如果待插入的元素是第一个元素,那么不需要将它的下一个元素的前驱指向被插入的这个元素(因为被插入的这个元素的下一个元素是空指针,不存在前驱),如果不是第一个元素,则进行头插即可。
//有参构造函数,用(数组+头插)的形式来构建空表
template<typename datatype>
two_way_list<datatype>::two_way_list(datatype a[], int n,int x)
{
head = new two_way_listnode<datatype>;
head->prior = NULL;
head->next = NULL;
for (int i = 0; i < n; i++)
{
two_way_listnode<datatype>* p = new two_way_listnode<datatype>;
p->value = a[i];
p->next = head->next;
if(head->next!=NULL)//判断是否是第一个新的节点
head->next->prior=p;
p->prior = head;
head->next = p;
}
}
有参构造函数(数组+尾插)
在尾部进行插入时,每次待插入元素的下一个元素都是空指针,因此不需要像头插那样有一步单独判断的过程,直接在尾部进行插入即可。值得注意的是,这里需要定义一个尾指针,并不断将其进行更新,以确保每次都是从尾部插入。
//有参构造函数,用(数组+尾插)的形式来构建链表
template<typename datatype>
two_way_list<datatype>::two_way_list(datatype a[], int n )
{
head = new two_way_listnode<datatype>;
head->prior = NULL;
head->next = NULL;
two_way_listnode<datatype>* tail = head;
for (int i = 0; i < n; i++)
{
two_way_listnode<datatype>* p = new two_way_listnode<datatype>;
p->value = a[i];
p->next = tail->next;
p->prior = tail;
tail->next = p;
tail = p;
}
}
打印双向链表
对双向链表进行遍历。
//打印双向链表
template <typename datatype>
void two_way_list<datatype>::printlist()
{
two_way_listnode<datatype>* cur = head->next;
while (cur != NULL)
{
cout << cur->value << "->";
cur = cur->next;
}
cout << "NULL" << endl;
}
在第i个节点插入
第i个节点插入的时候,要留意这个节点是不是最后一个节点,如果是最后一个节点,则有三条链需要被改变,如果不是最后一个元素,则有四条链会发生改变。也就是说,尾插的话需要单独处理。使用双向链表进行插入时,不需要提前找第i-1个节点,只需要找到第i个节点然后对其前驱和后继节点进行改变即可。
//双向链表在第i个节点插入,不需要提前找第i个节点(尾插需要单独处理)
template<typename datatype>
void two_way_list<datatype>::insert_two_way_list(int i, datatype e)
{
if (i < 1) throw"插入位置错误";
two_way_listnode<datatype>* cur = head->next;
int count = 1;
while (count < i && cur->next!= NULL)
{
cur= cur->next;
count++;
}
two_way_listnode<datatype>* p = new two_way_listnode<datatype>;
p->value = e;
if (cur->next == NULL)
{
p->next = NULL;
cur->next = p;
p->prior = cur;
}
else
{
p->next = cur;
p->prior = cur->prior;
cur->prior->next = p;
cur->prior = p;
}
}
求双向链表的长度
即对双向链表进行遍历,并记录链表的长度
//求双向链表的长度
template<typename datatype>
int two_way_list<datatype>::length()
{
int length = 0;
two_way_listnode<datatype>* cur = head->next;
while (cur != NULL)
{
cur = cur->next;
length++;
}
return length;
}
按位序查找
按照位置查找链表中相应的元素,如果找到,返回其元素值。
//按位序查找
template<typename datatype>
datatype two_way_list<datatype>::get(int i)
{
two_way_listnode<datatype>* cur = head->next;
int count = 1;
while (cur != NULL && count < i)
{
cur = cur->next;
count++;
}
if (cur == NULL) return 0x3f3f3f3f;
else return cur->value;
}
按值查找
在链表中查找相应的值所在的位置。
//按值查找
template<typename datatype>
int two_way_list<datatype>::locate(datatype x)
{
two_way_listnode<datatype>* cur = head->next;
int count = 1;
while (cur != NULL)
{
if (cur->value == x)
{
return count;
}
count++;
cur = cur->next;
}
if (cur->next == NULL) return -1;
}
判空
如果头指针的前驱和后继都是空指针,则链表为空,只有头指针。
//判空函数
template<typename datatype>
bool two_way_list<datatype>::empty()
{
if (head->next == NULL && head->prior == NULL) return false;
else return true;
}
删除双向链表的第i个节点
删除双向链表的第i个节点同样需要考虑是否是最后一个元素的情况,如果是最后一个元素,则无需考虑前驱链的指向,直接让它的上一个元素的next直接指向空指针即可,如果不是最后一个元素,那么需要有前驱和后继两条链的改变,让其正好跳过待删除的元素。
//删除双向链表的第i个节点
template<typename datatype>
void two_way_list<datatype>::delete_two_way_list(int i)
{
two_way_listnode<datatype>* cur = head->next;
int count = 1;
while (cur != NULL && count < i)
{
cur = cur->next;
count++;
}
if (cur == NULL) throw "位置有误";
if (cur->next != NULL)
{
cur->next->prior = cur->prior;
cur->prior->next = cur->next;
delete cur;
}
else if (cur->next == NULL)
{
cur->prior->next = NULL;
delete cur;
}
}
双向链表的析构函数
从头节点开始析构每一个节点,直到只有空指针,则析构完毕。
template<typename datatype>
two_way_list<datatype>::~two_way_list()
{
two_way_listnode<datatype>* cur = head;
while (cur != NULL)
{
head=head->next;
delete cur;
cur = head;
}
head = NULL;
}