文章目录
前言
双向变量同样也是在单链表的基础上进行改进。单链表存在着以下局限:
1.单链表的结点都只有一个指向下一个结点的指针;
2.单链表的数据元素无法直接访问其前驱元素。
对于访问前驱元素,我们或许会想到逆序访问,但是逆序访问单链表中的元素是极其耗时的操作!其时间复杂度可以达到O(n²)。
1.双向链表的定义
顾名思义,在双向链表的结点指针域中,有一个指向后继的指针域next,有一个指向前驱的指针域pre,其示意图如下:
2.双向链表的操作
双向链表拥有单链表的所有操作,但双向链表在单链表的基础上增加了一下操作:
1.获取当前游标指向的数据元素;
2.将游标重置指向链表中的第一个数据元素;
3.将游标移动指向到链表中的下一个数据元素;
4.将游标移动指向到链表中的上一个数据元素;
5.直接指定删除链表中的某个数据元素。
在进行操作前,我们要进行数据封装,结点指针域的定义,双向链表头结点的定义。
typedef void DLinkList;
//结点指针域定义,这里相当于一条地址链,一个地址里存着下一个地址,下一个地址存着下下个地址,以此类推。
typedef struct _tag_DLinkListNode DLinkListNode;
struct _tag_DLinkListNode
{
DLinkListNode* next;
DLinkListNode* pre;
};
typedef struct _tag_DLinkList //双向链表的头结点
{
DLinkListNode header; //指向下一元素的指针
DLinkListNode* slider; //游标(当前指针)
int length; //该双向链表的长度
} TDLinkList;
2.1创建
创建双向链表时,我们先动态地申请表头结点,令链表长度为0,指向前驱和后继的指针域指向空,游标指向空。
DLinkList* DLinkList_Create() //O(1)
{
TDLinkList* ret = (TDLinkList*)malloc(sizeof(TDLinkList)); //动态地申请表头节点
if (ret != NULL)
{
ret -> length = 0;
ret -> header.next = NULL;
ret -> header.pre = NULL;
ret -> slider = NULL;
}
return ret;
}
2.2销毁
直接free掉。
void DLinkList_Destroy(DLinkList* list) //O(1)
{
free(list);
}
2.3清空
让其回到初始状态,也就是长度为0,指向前驱和后继的指针域指向空,游标指向空。
void DLinkList_Clear(DLinkList* list) //O(1)
{
TDLinkList* sList = (TDLinkList*)list;
if (sList != NULL) //对双向链表的合法性检测
{
sList -> length = 0;
sList -> header.next = NULL;
sList -> header.pre = NULL;
sList -> slider = NULL;
}
}
2.4获取长度
int DLinkList_Length(DLinkList* list) //O(1)
{
TDLinkList* sList = (TDLinkList*)list;
int ret = -1;
if (sList != NULL) //对双向链表的合法性检测
{
ret = sList -> length;
}
return ret;
}
2.5插入元素
对于插入元素操作来说,由于多了一个前驱指针,所以在进行插入时,我们还要考虑前驱的操作,主要考虑插入的元素的位置是否为最后一个,插入元素的位置是否为第一个。
插入元素的操作示意图如下:
int DLinkList_Insert(DLinkList* list, DLinkListNode* node, int pos) //最好O(1),最坏O(n)
{
TDLinkList* sList = (TDLinkList*)list;
int ret = (sList != NULL) && (pos >= 0) && (node != NULL);
int i = 0;
if (ret)
{
DLinkListNode* current = (DLinkListNode*)sList; //让current指针指向表头
DLinkListNode* next = NULL;
for (i = 0; (i < pos) && (current -> next != NULL); i++)
{
current = current -> next;
}
next = current -> next;
current -> next = node;
node -> next = next;
if (next != NULL) //如果插入的元素位置不是最后一个
{
next -> pre = node;
}
node -> pre = current;
if (sList -> length == 0) //如果插入的元素位置是第一个
{
sList -> slider = node; //游标指向第一个元素
}
if (current == (DLinkListNode*)sList) //如果插入的元素指向表头
{
node -> pre = NULL;
}
sList -> length++;
}
return ret;
}
2.6获取元素
该操作和单链表中获取元素的操作几乎没什么区别。
DLinkListNode* DLinkList_Get(DLinkList* list, int pos) //最好O(1),最坏O(n)
{
TDLinkList* sList = (TDLinkList*)list;
DLinkListNode* ret = NULL;
int i = 0;
if ((sList != NULL) && (pos >= 0) && (pos < sList -> length))
{
DLinkListNode* current = (DLinkListNode*)sList; //让current指针指向表头
for (i = 0; i < pos; i++)
{
current = current -> next;
}
ret = current -> next; //返回指向元素的指针
}
return ret;
}
2.7删除元素
在删除元素的操作中,我们需要考虑的是被删除的元素是否为最后一个,第一个,游标指向的元素。该操作的示意图如下:
DLinkListNode* DLinkList_Delete(DLinkList* list, int pos) //最好O(1),最坏O(n)
{
TDLinkList* sList = (TDLinkList*)list;
DLinkListNode* ret = NULL;
int i = 0;
if ((sList != NULL) && (pos >= 0) && (pos < sList -> length))
{
DLinkListNode* current = (DLinkListNode*)sList; //让current指针指向表头
DLinkListNode* next = NULL;
for (i = 0; i < pos; i++)
{
current = current -> next;
}
ret = current -> next; //指向元素的指针
next = ret -> next;
current -> next = ret -> next;
if (next != NULL) //如果被删除元素的位置不是最后一个
{
next -> pre = current;
if (current == (DLinkListNode*)sList) //如果当前元素为第一个元素
{
next -> pre = NULL;
}
}
if (sList -> slider == ret) //若被删除的元素为游标所指向的元素
{
sList -> slider = next;
}
sList -> length--;
}
return ret;
}
2.8删除指定元素
在该操作中,我们首先对链表进行合法性检测,然后找到指定元素的逻辑位置,最后将它删除掉。
DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node)
{
TDLinkList* sList = (TDLinkList*)list;
DLinkListNode* ret = NULL;
int i;
if (sList != NULL)
{
DLinkListNode* current = (DLinkListNode*)sList; //让current指针指向表头
for (i = 0; i < sList -> length; i++)
{
if (current -> next == node)
{
ret = current -> next;
break;
}
current = current -> next;
}
if (ret != NULL)
{
DLinkList_Delete(list, i);
}
}
return ret;
}
2.9重置游标
将游标重置指向链表中的第一个元素。
DLinkListNode* DLinkList_Reset(DLinkList* list)
{
TDLinkList* sList = (TDLinkList*)list;
DLinkListNode* ret = NULL;
if (sList != NULL)
{
sList -> slider = sList -> header.next;
ret = sList -> slider;
}
return ret;
}
2.10获取当前游标指向的元素
DLinkListNode* DLinkList_Current(DLinkList* list)
{
TDLinkList* sList = (TDLinkList*)list;
DLinkListNode* ret = NULL;
if (sList != NULL)
{
ret = sList -> slider;
}
return ret;
}
2.11移动游标指向下一个元素
DLinkListNode* DLinkList_Next(DLinkList* list)
{
TDLinkList* sList = (TDLinkList*)list;
DLinkListNode* ret = NULL;
if ((sList != NULL) && (sList -> slider != NULL))
{
ret = sList -> slider;
sList -> slider = ret -> next;
}
return ret;
}
2.12移动游标指向上一个元素
DLinkListNode* DLinkList_Pre(DLinkList* list)
{
TDLinkList* sList = (TDLinkList*)list;
DLinkListNode* ret = NULL;
if ((sList != NULL) && (sList -> slider != NULL))
{
ret = sList -> slider;
sList -> slider = ret -> pre;
}
return ret;
}
3.测试
对双向链表进行常规测试,代码如下:
#include <stdio.h>
#include <stdlib.h>
#include "DLinkList.h"
typedef struct Value
{
DLinkListNode header;
int v;
} Val;
int main(int argc, char *argv[])
{
int i;
Val v1, v2, v3, v4, v5;
Val* pv = NULL;
v1.v = 1;
v2.v = 2;
v3.v = 3;
v4.v = 4;
v5.v = 5;
DLinkList* list = DLinkList_Create();
DLinkList_Insert(list, (DLinkListNode*)&v1, DLinkList_Length(list));
DLinkList_Insert(list, (DLinkListNode*)&v2, DLinkList_Length(list));
DLinkList_Insert(list, (DLinkListNode*)&v3, DLinkList_Length(list));
DLinkList_Insert(list, (DLinkListNode*)&v4, DLinkList_Length(list));
DLinkList_Insert(list, (DLinkListNode*)&v5, DLinkList_Length(list));
for (i = 0; i < DLinkList_Length(list); i++)
{
pv = (Val*)DLinkList_Get(list, i);
printf("%d\n", pv -> v);
}
printf("\n");
DLinkList_Delete(list, DLinkList_Length(list) - 1); //删除最后一个元素
DLinkList_Delete(list, 0); //删除第一个元素
for (i = 0; i < DLinkList_Length(list); i++)
{
pv = (Val*)DLinkList_Next(list);
printf("%d\n", pv -> v);
}
printf("\n");
DLinkList_Reset(list);
DLinkList_Next(list);
pv = (Val*)DLinkList_Current(list);
printf("%d\n", pv -> v);
DLinkList_DeleteNode(list, (DLinkListNode*)pv);
pv = (Val*)DLinkList_Current(list);
printf("%d\n", pv -> v);
DLinkList_Destroy(list);
return 0;
}
得到的结果为: