双向链表
⚪接口设计
双向链表和单向链表对应的接口无太多差别,只是在节点类中添加了prev指针和在链表类中添加了last尾指针
#pragma once
#pragma once
#include <iostream>
using namespace std;
template<class E>
class Node
{
public:
E element;
Node<E>* next;
Node<E>* prev;
Node(Node<E>* prev, E element, Node<E>* next)
{
this->element = element;
this->next = next;
this->prev = prev;
}
~Node() {}
};
template<class E>
class Circle_DouLinkList
{
private:
int size;
Node<E>* first = NULL;
Node<E>* last = NULL;
static const int ELEMENT_NOT_FOUND = -1;
Node<E>* find_node(int index);
public:
void clear();
void add(E element);
void add(int index, E element);
int Size();
int indexOf(E element);
bool isEmpty();
bool contains(E element);
E get(int index);
E set(int index, E element);
E remove(int index);
void print();
~Circle_DouLinkList(){}
};
⚪难点讲解
①添加元素void add(int index, E element);
因为涉及头节点和尾节点,所以要分中间、头、尾三种情况来讨论
1、中间
假设在位置2处添加元素
首先将添加位置2节点的前一个节点1处的next指针指向新元素,因为存在prev指针,所以不需要再像单向链表中一样再去找前一个节点,只需要通过结点2的prev指针就可以找到前一个节点。然后将节点2的prev指针指向新元素。最后将所添加节点的prev指针指向节点1,next指针指向节点2。效果如下图所示。
Node<E>* next = find_node(index);
Node<E>* prev = next->prev;
Node<E>* p = new Node<E>(prev, element, next);
prev->next = p;
next->prev = p;
2、头部
由于前面的代码设计的prev指针,所以要考虑prev指针为空的情况,即在头节点添加元素。因此我们要加上对于在头节点添加元素的讨论。
Node<E>* next = find_node(index); //0
Node<E>* prev = next->prev; //NULL
Node<E>* p = new Node<E>(prev, element, next);
next->prev = p;
if (prev == NULL) //如果index==0
{
first = p;
}
else
{
prev->next = p;
}
与上面在中间节点添加元素是一个思路,只需在在对于prev的操作进行一个讨论,前面的代码可以共用。将往节点0处添加节点为例子 。
第一句代码next指针 获得的是节点0 ,第二句代码prev指针获得的是节点0的前一个节点,便是NULL。然后将prev,element,next分别赋值给新节点p,等价于Node* p = new Node(NULL, element, 0),然后将next指针即节点0的prev指向新节点,最后判断prev节点是否为空,即是否插入在头部,如果是在头部就将头指针指向新节点。
3、尾部
由于还存在尾节点,因此我们仍需要对于尾节点的情况进行讨论
首先要将尾节点指向新添加的元素顺便将新元素构造出来。新添加节点的prev指针就是之前的尾指针,next指针就是NULL指针。
last = new Node<E>(last, element, NULL);
最后只剩下之前的尾节点的next指针指向新添加节点的这一条线。若是使用last->next=p这一句代码,则last指针在之前前面的代码就已指向新的元素,因此需要创建一个新的临时指针Node *Old_last = last;来保存之前的最后一个节点。
Node<E> *Old_last = last;
last = new Node<E>(Old_last, element, NULL);
Old_last->next = last;
ps:也可以通过新的last->prev来获取原先的尾节点
但是讨论到这里的代码的健壮性仍是不够完善,因为用到了next指针而未考虑到next指针为NULL的可能性 ,即当是一个空链表(一个元素都未添加的情况时)
因此仍旧需要再增加一个当添加元素为第一个元素的讨论
因为是添加的第一个元素,所以应该将first和last都指向这个新元素,而在前面的代码已经实现其余的连接,即在last = new Node(Old_last, element, NULL);代码中代表的就是last = new Node(NULL, element, NULL);,因此只需要添加一句first=last即可
Node<E> *Old_last = last;
last = new Node<E>(Old_last, element, NULL);
if (Old_last == NULL) //如果是链表添加的第一个元素
{
first = last;
}
else
{
Old_last->next = last;
}
4、总代码
template<class E>
void DouLinkList<E>::add(int index, E element) //在某节点添加元素
{
if (index == size) //在尾节点添加元素
{
Node<E> *Old_last = last;
last = new Node<E>(Old_last, element, NULL);
if (Old_last == NULL) //如果是链表添加的第一个元素
{
first = last;
}
else
{
Old_last->next = last;
}
}
else
{
Node<E>* next = find_node(index);
Node<E>* prev = next->prev;
Node<E>* p = new Node<E>(prev, element, next);
next->prev = p;
if (prev == NULL) //如果index==0
{
first = p;
}
else
{
prev->next = p;
}
}
size++;
}
①删除元素remove(int index)
1、中间节点
假设删除的是位置2的节点。与单链表的删除方法相似,只需要用一个节点保存被删除节点的元素来返回,然后找到要删除节点的前一个节点,将前一个节点的next指针指向下下一个节点,多了一点的是需要将所删除节点的prev指针指向删除节点的前一个节点,最后再把所删除的节点delete就可以了。
2、 头、尾节点
对于头与尾删除的特殊情况就在于头节点的prev指针指向为空和尾节点的next指针为空
对于头节点是将first指针指向删除节点0的下一个节点1,再将节点1的prev指针指向NULL,而后一句的代码利用中间节点删除的代码就可完成。
而尾节点的删除与头节点的删除几乎无区别,就不多赘述了。
3、总代码
template<class E>
E LinkList<E>::remove(int index) //移除某个元素,返回被移除的元素 要讨论是否为头节点
{
Node<E>* p; //创建一个节点,用来返回被移除的元素
if (index == 0) //若为头节点直接指向下个节点
{
p = first;
first = first->next;
size--;
}
else //若不是头节点,让上一个节点指向这个节点的下一个节点
{
Node<E>* prev = find_node(index - 1); //获得前一个节点
p = prev->next; //获得index节点
prev->next = prev->next->next; //前一个节点指向index后面这个节点
size--;
}
E x = p->element;
delete p;
return x;
}
⚪总代码
Dou_Link_List.h
#pragma once
#include <iostream>
using namespace std;
template<class E>
class Node
{
public:
E element;
Node<E>* next;
Node<E>* prev;
Node(Node<E>* prev,E element, Node<E>* next)
{
this->element = element;
this->next = next;
this->prev = prev;
}
~Node() {}
};
template<class E>
class DouLinkList
{
private:
int size;
Node<E>* first=NULL;
Node<E>* last=NULL;
static const int ELEMENT_NOT_FOUND = -1;
Node<E>* find_node(int index);
public:
void clear(); //清除所有元素
void add(E element); //在末尾添加元素
void add(int index, E element); //在index处添加元素
int Size(); //求size
int indexOf(E element); //求某元素的位置
bool isEmpty(); //链表是否为空
bool contains(E element); //某元素是否存在
E get(int index); //获得某节点的元素
E set(int index, E element); //设置某节点的元素
E remove(int index); //删除某节点的元素
void print(); //打印
~DouLinkList() {}
};
Dou_Link_List.cpp
#include"Dou_LinkList.h"
template<class E>
void DouLinkList<E>::clear() //清除所有元素
{
Node* q;
Node<E>* p = first->next;
while (p != NULL)
{
q = p;
p = p->next;
delete q;
}
p = NULL;
q = NULL;
}
template<class E>
int DouLinkList<E>::Size() //求出链表的大小
{
return size;
}
template<class E>
bool DouLinkList<E>::isEmpty() //链表是否为空
{
return size == 0;
}
template<class E>
bool DouLinkList<E>::contains(E element) //某元素是否存在
{
return indexOf(element) != -ELEMENT_NOT_FOUND;
}
template<class E>
void DouLinkList<E>::add(E element) //在尾节点添加元素
{
add(size, element);
}
template<class E> //获得某元素的位置
int DouLinkList<E>::indexOf(E element)
{
if (element == NULL)
{
Node<E>* node = first;
for (int i = 0; i < size; i++)
{
if (node->element == NULL)
{
return i;
}
node = node->next;
}
}
else
{
Node<E>* node = first;
for (int i = 0; i < size; i++)
{
if (node->element==element)
{
return i;
}
node = node->next;
}
}
return ELEMENT_NOT_FOUND;
}
template<class E>
E DouLinkList<E>::get(int index) //获得某节点的元素
{
return find_node(index).element;
}
template<class E>
E DouLinkList<E>::set(int index, E element) //更改设置某节点元素
{
Node<E>* node = find_node(index);
E old = node->element;
node->element = element;
return old;
}
template<class E>
void DouLinkList<E>::add(int index, E element) //在某节点添加元素
{
if (index == size) //在尾节点添加元素
{
Node<E> *Old_last = last;
last = new Node<E>(Old_last, element, NULL);
if (Old_last == NULL) //如果是链表添加的第一个元素
{
first = last;
}
else
{
Old_last->next = last;
}
}
else
{
Node<E>* next = find_node(index);
Node<E>* prev = next->prev;
Node<E>* p = new Node<E>(prev, element, next);
if (prev == NULL) //如果index==0
{
first = p;
}
else
{
prev->next = p;
}
next->prev = p;
}
size++;
}
template<class E>
E DouLinkList<E>::remove(int index) //删除某节点的元素
{
Node<E>* p = find_node(index);
Node<E>* prev = p->prev;
Node<E>* next = p->next;
if (prev == NULL) //index==0
{
first = next;
}
else
{
prev->next = next;
}
if (next == NULL) //index==size-1
{
last = prev;
}
else
{
next->prev = prev;
}
E x = p->element;
delete p;
return x;
}
template<class E>
void DouLinkList<E>::print()
{
Node<E>* p = first;
while (p!=NULL)
{
cout << p->element << "->" ;
p = p->next;
}
}
template<class E>
Node<E>* DouLinkList<E>::find_node(int index)
{
if (index < 0 || index >= size)
{
throw "index不符合规范";
}
if (index < (size >> 1))
{
Node<E>* node = first;
for (int i = 0; i < index; i++)
{
node = node->next;
}
return node;
}
else
{
Node<E>* node = last;
for (int i = size-1; i > index; i--)
{
node = node->prev;
}
return node;
}
}
main.cpp
#include <iostream>
#include "Dou_LinkList.h"
#include "Dou_LinkList.cpp"
int main()
{
DouLinkList<int> d_list;
d_list.add(10);
d_list.add(20);
d_list.add(30);
d_list.add(40);
d_list.add(50);
d_list.add(60);
d_list.add(70);
d_list.print();
cout << endl;
cout << d_list.remove(2) << endl;
cout << d_list.set(3,10) << endl;
cout << d_list.indexOf(60) << endl;
d_list.print();
return 0;
}
⚪对比
动态数组和双向链表的对比和使用
附:
1.本博客是由学习小码哥视频所总结文章
2.代码都通过合格检测,请放心食用~