您的位置:首页 > 理论基础 > 数据结构算法

数据结构-02-链表数据结构之双链表和循环链表

2017-11-07 18:59 369 查看
上篇博客分析了单链表:数据结构-01-链表数据结构之单链表,这篇博客将分析链表的其他几种常见形式

本文中的所有代码均在github上的项目中:List_DataStructure

双链表

双链表也叫双向链表,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。

双链表是最常用的一种链表形式,实际应用中双链表一般会被创建成环状形式,即头尾结点分别存储对方的地址,链表形成了一个封闭的环。

1.双链表的基本结构:

双链表的开始是一个头指针,每个结点中都有指向下一结点及上一结点的地址



双链表的C++实现:

1.双链表的结点类

template<typename Type> class DoubleLinkedList;

template<typename Type>
class DoubleLinkedListNode
{
private:
friend typename DoubleLinkedList<Type>;
//构造函数和析构函数
DoubleLinkedListNode() :pprior(nullptr), pnext(nullptr) {}
DoubleLinkedListNode(const Type item, DoubleLinkedListNode<Type> * next = nullptr, DoubleLinkedListNode<Type> * prior = nullptr)
:data(item), pnext(next), pprior(prior) {}
~DoubleLinkedListNode() {
pprior = nullptr;
pnext = nullptr;
}
public:
//单链表节点对象的基本方法
Type GetData();
private:
Type data;
DoubleLinkedListNode * pprior;
DoubleLinkedListNode * pnext;
};


2.双链表类

template <typename Type>
class DoubleLinkedList
{
public:
DoubleLinkedList() :head(new DoubleLinkedListNode<Type>()) {
head->pprior = head;
head->pnext = head;
}
~DoubleLinkedList() {
MakeEmpty();
delete head;
}
//对双链表的基本操
void MakeEmpty();
void Insert(Type item, int i = 0);
void Remove(int i = 0);
void RemoveAll(Type item);
//返回双聊表的基本信息
int length();
Type Get(int i);
//查找和排序
DoubleLinkedListNode<Type> * SequentialSearch(Type item);
//基础功能
void Print();
private:
//链表的头节点是私有的
SingleLinkedListNode<Type> *head;
};


2.双链表的初始化:

双链表的空参构造函数:

DoubleLinkedList() :head(new DoubleLinkedListNode<Type>()) {
head->pprior = head;
head->pnext = head;
}


根据双链表在创建时调用空参构造函数,创建了一个结点对象,即链表的头结点,头结点的上一结点的地址pprior 和下一节点的地址pnext 存储均存储头结点地址



3.向双链表中插入数据:

向双向循环链表中插入数据时应注意链表是封闭环状,所以使用循环或递归时要注意结束条件

//向双链表中插入元素
template <typename Type>
void DoubleLinkedList<Type>::Insert(Type item, int i = 0) {
if (i < 0){
cout << "请输入一个大于0的数" << endl;
}
DoubleLinkedListNode<Type> *pmove = head;
DoubleLinkedListNode<Type> *pnode = new DoubleLinkedListNode<Type>(item);
for (int j = 0; j < i; j++){
pmove = pmove->pnext;
if (pmove == head) {
cout << "i值越界,链表没有那么长!" << endl;
//结束程序
exit(1);
}
}
pnode->pnext = pmove->pnext;
pnode->pnext = pmove;
pmove->pnext = pnode;
pnode->pnext->pprior = pnode;
}


1.初始化一个结点类的对象pnode

2.创建一个结点类型指针pmove,用于遍历找到插入点位置

3.循环找到插入点位置,若pmove == head说明指定的插入点位置超过了链表长度

4.执行插入

pnode->pnext = pmove->pnext; //步骤一
pnode->pprior= pmove; //步骤二
pmove->pnext = pnode; //步骤三
pnode->pnext->pprior = pnode; //步骤四


如下图所示,pnode为x,即待插入节点

pmove指向节点ai

步骤一:将ai+1的地址(即ai->pnext也是pmove->pnext)赋值给pnode的下一结点地址pnext

步骤二:将ai的地址(即pmove)赋值给pnode的上一结点地址pprior

步骤三:将pnode的地址赋值给ai(即pmove)的下一结点地址pnext

步骤四:将pnode的地址赋值给ai+1(即ai+1->pprior也是pnode->pnext->pprior)的上一结点地址pprior



4.删除双链表中的数据:

//删除双链表中的元素
template <typename Type>
void DoubleLinkedList<Type>::Remove(int i = 0) {
if (i < 0) {
cout << "请输入一个大于0的数" << endl;
}
DoubleLinkedListNode<Type> *pmove = head, *pdel;
for (int j = 0; j < i; j++) {
pmove = pmove->pnext;
//当pmove == head,说明pmove移动了一个链表长度,又绕回了链表起始点
if (pmove == head) {
cout << "i值越界,链表没有那么长!" << endl;
//结束程序
exit(1);
}
}
pdel = pmove;
pmove->pprior->pnext = pdel->pnext;
pmove->pnext->pprior = pdel->pprior;
Type temp = pdel->data;
cout << "删除的结点中保存的数据是:" << temp << endl;
delete pdel;
}


删除链表的结点必须要两个指向待删除结点的指针配合

1.创建两个结点类型指针pmove,和pdel,pmove用于遍历找到待删除结点

2.循环找到插入点位置,若pmove == head说明指定的待删除结点位置超过了链表长度

3.执行删除

pdel = pmove; //步骤一
pmove->pprior->pnext = pdel->pnext; //步骤二
pmove->pnext->pprior = pdel->pprior; //步骤三


如下图所示,pmove指向节点ai,即待删除节点

步骤一:通过赋值使pdel指向节点ai

步骤二:将ai+1的地址赋给ai-1的下一结点地址pnext(即ai-1->pnext也是pmove->pprior->pnext)

步骤三:将ai-1的地址赋给ai+1的上一结点地址pprior(即ai+1->pprior也是pmove->pnext->pprior)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: