前言
T_T此专栏用于记录数据结构及算法的(痛苦)学习历程,便于日后复习(这种事情不要啊)。所用教材为《数据结构 C语言版 第2版》严蔚敏。有关线性表的顺序存储见线性表概念及顺序表的实现,有关单链表见线性表的链式存储(单链表)
一、双向链表是什么?
双向链表:在链表的结点中有两个指针域,一个指向直接后继,另一个指向直接前驱。如下图所示(图源亿速云)。对于头结点,无直接前驱结点,故prev为NULL;对于最后一个结点,无直接后继结点,故next为NULL。
二、双向循环链表的意义
以上讨论的链式存储结构的结点中只有一个指示直接后继的指针域,由此,从某个结点出发只能顺指针向后寻查其他结点。若要寻查结点的直接前驱,则必须从表头指针出发。换句话说,在单链表中,查找直接后继结点的执行时间为 0(1), 而查找直接前驱的执行时间为O(n)。为克服单链表这种单向性的缺点,可利用双向链表 (Double Linked List)。
下面给出双向链表结点结构定义:
//- - - - -双向链表的存储结构-----
typedef struct DuLNode
ElemType data; //数据域
struct DuLNode *prior; //直接前驱
struct DuLNode *next; //直接后继
}DuLNode,*DuLinkList;
三、双向链表基本操作的实现
双向链表的操作与单链表操作基本相同,除了初始化头指针、创建链表、插入结点和删除结点。因此重点关注以上四项的操作即可。有关单链表见线性表的链式存储(单链表)
下面给出双向链表基本操作的实现:
#include <iostream>
using namespace std;
#define OK 1
#define fail 0
#define overflow -1
typedef int Status;
typedef int Elemtype;
typedef struct DuLnode {
Elemtype data;
DuLnode * prir;
DuLnode * next;
}DuLnode, * DuLlist;
DuLlist L;
Elemtype e;
//初始化双向链表,创建头指针和头结点
Status InitDuList(DuLlist& L)
{
L = new DuLnode; //头指针
if (!L)return fail; //如果创建失败
L->data = 123456; //头结点
L->next = NULL;
L->prir = NULL;
return OK;
}
//创建双向链表,头插法,创建i个结点
//先创建的后访问
Status CreatDuList_Head(DuLlist L, int i)
{
if (i < 1)return fail; //至少创建1个结点
while (i--)
{
DuLnode* p = new DuLnode;
cin >> p->data;
L->next->prir = p;
p->prir = L;
p->next = L->next;
L->next = p;
}
return OK;
}
//创建双向链表,尾插法,创建i个结点
//先创建的先访问
Status CreatDuList_End(DuLlist L, int i)
{
DuLlist p = L;
if (i < 1)return fail; //至少创建1个结点
while (i--)
{
DuLnode* q = new DuLnode;
cin >> q->data;
q->prir = p;
q->next = NULL;
p->next = q;
p = q;
}
return OK;
}
//得到第i个结点的值,注意首元结点开始算第一个结点
//e为得到的结点数据值
Status GetElem(DuLlist L, int i, Elemtype* e)
{
DuLlist p = L;
if (i < 1 || L->next == NULL)return overflow; //如果i不合法或链表为空,则失败
for (i; i > 0; i--)
{
if (p->next != NULL)
p = p->next;
else
return overflow;
}
*e = p->data;
return OK;
}
//已知地址为p的结点,查找其直接前驱存储的数据,注意首元结点开始算第一个结点
//e为得到的结点数据值
Status GetElemPrir(DuLlist L, DuLnode *q, Elemtype* e)
{
if ( q->prir == L)return overflow; //如果只有一个结点,则失败
*e = q->prir->data;
return OK;
}
//查找某个值在链表中的位置,注意首元结点开始算第一个结点
//e为要查找的数据值,ElemPosition为在链表中的位置
DuLnode* LocatElem(DuLlist L, Elemtype e, int& ElemPosition)
{
ElemPosition = 1;
DuLlist p = L->next;
while (p && p->data != e)
{
p = p->next;
ElemPosition++;
}
return p;
}
//在第i个位置前插入一个结点,注意首元结点开始算第一个结点
//e插入的结点的数据值
Status InsertElem(DuLlist L, int i, Elemtype e)
{
DuLlist p = L;
if (i < 1)return fail; //如果i不合法则失败
while (p && i > 1)
{
p = p->next;
i--;
}
if (!p || i != 1)return fail; //如果链表不存在第i个位置,则失败
DuLnode* q = new DuLnode;
q->data = e;
q->prir = p;
q->next = p->next;
p->next->prir = q;
p->next = q;
return OK;
}
//删除在第i个位置的结点,注意首元结点开始算第一个结点
Status DeleteElem(DuLlist L, int i)
{
DuLlist p = L;
if (i < 1)return fail; //如果i不合法则失败
while (p && i > 1)
{
p = p->next;
i--;
}
if (!p || i != 1)return fail; //如果链表不存在第i个位置,则失败
DuLlist q = p->next;
p->next = q->next;
q->next->prir = p;
delete q;
q = NULL;
return OK;
}
//销毁链表
Status DestoryDuList(DuLlist L)
{
DuLlist p = L;
while (p)
{
DuLlist q = p;
p = p->next;
delete q;
q = NULL;
}
return OK;
}
int main()
{
InitDuList(L);
cout << L << endl;
cout << L->data << endl;
cout << L->next << endl;
cout << L->prir << endl;
CreatDuList_End(L, 5);
cout << GetElemPrir(L,L->next->next,&e) << endl;
cout << e << endl;
cout << InsertElem(L, 3, 6) << endl;
cout << GetElemPrir(L, L->next->next->next, &e) << endl;
cout << e << endl;
cout << GetElem(L, 3, &e) << endl;
cout << e << endl;
return 0;
}
测试现象:
总结
路漫漫其修远兮,吾将上下而摆烂。
有任何疑问和补充,欢迎交流。(但我显然不会)