数据结构之线性表——双向链表

来源:互联网 发布:php 随机昵称 编辑:程序博客网 时间:2024/06/06 12:58

1为什么需要双向链表?

单链表的结点都只有一个指向下一个节点的指针,单链表的数据元素无法直接访问其前驱元素,逆序访问单链表中的元素是极其耗时的操作。所以必要的时候可以很大程度上减少访问前驱元素的的消耗。

2 双向链表定义:

在单链表中增加一个指向其前驱的pre指针。双向链表的结构示意图如下如所示

3链表的具体实现分析:

(1)双向链表的节点指针域定义:

节点的指针域包含一个指向后继的next指针,和一个指向前驱的pre指针。如下所示:

typedef struct _tag_DLinkListNode{struct _tag_DLinkListNode * next;struct _tag_DLinkListNode * pre;}DLinkListNode;
(2)双向链表的头节点定义:

头节点中包含一个header的指针域,和一个游标指针slider,以及链表的长度length。如下所示:

typedef struct _tag_DLinkList{DLinkListNode header;DLinkListNode *slider;int length;}TDLinkList;
(3)双向链表的数据元素节点示例定义

数据元素节点包含指针域node,以及数据域v。如下所示:

struct Value{DLinkListNode node;int v;};
(4)创建双向链表

申请头结点的动态内存,并且初始化数据:header.next = NULL;header.pre = NULL;slider = NULL;length = 0;如下所示:

DLinkList* DLinkList_Create(){TDLinkList * ret = (TDLinkList *)malloc(sizeof(TDLinkList));if (ret != NULL){ret->header.next = NULL;ret->header.pre = NULL;ret->length = 0;ret->slider = NULL;}return ret;}
(5)销毁双向链表

由于数据元素节点的生命周期不是由链表算法管理的,所以只需要销毁申请的头节点元素即可。

//销毁双向链表void DLinkList_Destory(DLinkList *list){if (list != NULL){free(list);}}
(6)清空双向链表

由于双向链表的的数据元素节点的生命周期不是由链表算法管理的,所以只需要将头节点中的数据初始化即可。

//清空双向链表void DLinkList_Clear(DLinkList *list){TDLinkList *sList = (TDLinkList *)list;if (sList != NULL){sList->length = 0;sList->header.next = NULL;sList->header.pre = NULL;sList->slider = NULL;}}
(7)获取双向链表的长度

双向链表的长度只要返回头结点的length元素即可。

//双向链表长度int DLinkList_Length(DLinkList *list){TDLinkList * sList = (TDLinkList *)list;int ret = -1;if (sList != NULL){ret = sList->length;}return ret;}
(8)双向链表的插入操作

双向链表的插入插入要考虑在0号位置插入和插入第一个节点以及普通茶如三种情况:

          a) 双向链表插入的是第一个元素情况:

头结点的next指针指向第一个节点,pre指针指向空,slider指针指向第一个节点,第一个节点的pre和next指针都指向NULL。


        b)双向链表插入位置为第0号位置情况:

第一步:将current指针指向头结点,next指针指向第0号位置节点。

第二步:将头结点的next指针指向node节点。

第三步:将Node节点的next指针指向0号位置的节点。

第四步:将0号位置的pre指针指向node节点。

第五步:将头结点的slider指针指向node节点。

第六步:将node节点的pre指针指向NULL。


c)双向链表普通插入情况:

假设在1号位置之后插入node节点:首先current指针指向1号节点,next指针指向2号节点。

第一步:node节点的next指针指向2号节点

第二步:1号节点的next指针指向node节点

第三步:node节点的pre指针指向1号节点

第四步:2号节点的pre指针指向node节点


//双向链表插入int DLinkList_Insert(DLinkList *list, DLinkListNode *node, int pos){int ret = 0, i = 0;TDLinkList *sList = (TDLinkList *)list;if (list == NULL || node == NULL || pos < 0){return -1;}if (pos > DLinkList_Length(list)){printf("非法的元素的插入 DLinkList_Insert() error !\n");return -1;}DLinkListNode * current = (DLinkListNode *)sList;DLinkListNode * next = NULL;//需要增加next指针for (i = 0; (i < pos) && (current->next != NULL); i++){current = current->next;}next = current->next;//步骤1-2current->next = node;node->next = next;//步骤3-4if (next != NULL)//插入的是第一个元素{next->pre = node;}node->pre = current;if (sList->length == 0){sList->slider = node;//处理游标}//若在0号位置插入,需要特殊处理 新来节点next和pre指向nullif (current == (DLinkListNode *)sList){node->pre = NULL;}sList->length ++;return ret;}

(9)双向链表获取某一个位置的节点

直接返回该位置的节点。

//获取某一个节点DLinkListNode * DLinkList_Get(DLinkList *list, int pos){TDLinkList *sList = (TDLinkList *)list;DLinkListNode * ret = NULL;if ((sList != NULL) &&( pos >= 0) && (pos < sList->length)){DLinkListNode * current = (DLinkListNode *)sList;for (int i = 0; i < pos; i++){current = current->next;}ret = current->next;}return ret;}
(10)双向链表删除某一个位置的节点

双向链表删除某一个位置的节点包含:删除0号位置的节点和删除普通位置的节点

a)删除0号位置节点:current指针指向头结点位置,next指针指向一号节点位置

第一步:将current指向节点的next域指向一号节点位置。

第二步:将一号节点的pre域指向NULL

第三步:将头结点的slider指针指向一号节点位置。

第四步:删除0号节点,链表长度减一


b)删除普通位置节点:

假如删除2号位置节点:current指针指向一号节点位置,next指针指向3号节点位置

第一步:curren指向节点的next域指向三号节点。

第二步:next指向节点的pre域指向一号节点。

第三步:删除二号节点,链表长度减一

//删除节点DLinkListNode * DLinkList_Delete(DLinkList *list, int pos){TDLinkList *sList = (TDLinkList *)list;DLinkListNode * ret = NULL;int i = 0;if (sList == NULL || pos < 0){return NULL;}if ((sList != NULL) && (0 <= pos) && (pos <= sList->length)){DLinkListNode * current = (DLinkListNode *)sList;DLinkListNode * next = NULL;for (i = 0; i < pos; i++){current = current->next;}ret = current->next;next = ret->next;//步骤一current->next = 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;}
(11)双向链表删除某一个元素
在链表中逐个匹配找到元素相同的节点删除。
//删除某一个元素DLinkListNode * DLinkList_DeleteNode(DLinkList * list, DLinkListNode *node){TDLinkList * sList = (TDLinkList *)list;DLinkListNode * ret = NULL;int i = 0;if (sList != NULL){DLinkListNode * current = (DLinkListNode *)sList;for (i = 0; i < sList->length; i++){if (current->next = node){ret = current->next;break;}}if (ret != NULL){DLinkList_Delete(sList, i);}}return ret;}
(12)重置游标

将游标的位置重新指向第一个数据元素位置

//重置游标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;}
(13)获取游标当前位置指向的元素

直接返回游标指向的节点

//获取游标当前位置DLinkListNode * DLinkList_Current(DLinkList *list){TDLinkList * sList = (TDLinkList *)list;DLinkListNode * ret = NULL;if (sList != NULL){ret = sList->slider;}return ret;}
(14)双向链表游标下移
//游标下移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;}
(15)双向链表游标前移
//游标前移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;}
4 代码实现以及测试案例
//DLinkList.h#ifndef _DLINKLIST_H_#define _DLINKLIST_H_typedef void DLinkList;typedef struct _tag_DLinkListNode{struct _tag_DLinkListNode * next;struct _tag_DLinkListNode * pre;}DLinkListNode;//创建双向链表DLinkList *DLinkList_Create();//销毁双向链表void DLinkList_Destory(DLinkList *list);//清除双向链表void DLinkList_Clear(DLinkList *list);//双向链表长度int DLinkList_Length(DLinkList *list);//双向链表插入int DLinkList_Insert(DLinkList *list, DLinkListNode *node, int pos);//获取某一个节点DLinkListNode * DLinkList_Get(DLinkList *list, int pos);//删除节点DLinkListNode * DLinkList_Delete(DLinkList *list, int pos);//--add//删除某一个元素DLinkListNode * DLinkList_DeleteNode(DLinkList * list, DLinkListNode *node);//重置游标DLinkListNode * DLinkList_Reset(DLinkList *list);//获取游标当前位置DLinkListNode * DLinkList_Current(DLinkList *list);//游标下移DLinkListNode * DLinkList_Next(DLinkList *list);//游标前移DLinkListNode * DLinkList_Pre(DLinkList *list);#endif

//DLinkList.cpp#include"DLinkList.h"#include<stdio.h>#include<malloc.h>typedef struct _tag_DLinkList{DLinkListNode header;DLinkListNode *slider;int length;}TDLinkList;//创建双向链表DLinkList* DLinkList_Create(){TDLinkList * ret = (TDLinkList *)malloc(sizeof(TDLinkList));if (ret != NULL){ret->header.next = NULL;ret->header.pre = NULL;ret->length = 0;ret->slider = NULL;}return ret;}//销毁双向链表void DLinkList_Destory(DLinkList *list){if (list != NULL){free(list);}}//清空双向链表void DLinkList_Clear(DLinkList *list){TDLinkList *sList = (TDLinkList *)list;if (sList != NULL){sList->length = 0;sList->header.next = NULL;sList->header.pre = NULL;sList->slider = NULL;}}//双向链表长度int DLinkList_Length(DLinkList *list){TDLinkList * sList = (TDLinkList *)list;int ret = -1;if (sList != NULL){ret = sList->length;}return ret;}//双向链表插入int DLinkList_Insert(DLinkList *list, DLinkListNode *node, int pos){int ret = 0, i = 0;TDLinkList *sList = (TDLinkList *)list;if (list == NULL || node == NULL || pos < 0){return -1;}if (pos > DLinkList_Length(list)){printf("非法的元素的插入 DLinkList_Insert() error !\n");return -1;}DLinkListNode * current = (DLinkListNode *)sList;DLinkListNode * next = NULL;//需要增加next指针for (i = 0; (i < pos) && (current->next != NULL); i++){current = current->next;}next = current->next;//步骤1-2current->next = node;node->next = next;//步骤3-4if (next != NULL)//插入的是第一个元素{next->pre = node;}node->pre = current;if (sList->length == 0){sList->slider = node;//处理游标}//若在0号位置插入,需要特殊处理 新来节点next和pre指向nullif (current == (DLinkListNode *)sList){node->pre = NULL;}sList->length ++;return ret;}//获取某一个节点DLinkListNode * DLinkList_Get(DLinkList *list, int pos){TDLinkList *sList = (TDLinkList *)list;DLinkListNode * ret = NULL;if ((sList != NULL) &&( pos >= 0) && (pos < sList->length)){DLinkListNode * current = (DLinkListNode *)sList;for (int i = 0; i < pos; i++){current = current->next;}ret = current->next;}return ret;}//删除节点DLinkListNode * DLinkList_Delete(DLinkList *list, int pos){TDLinkList *sList = (TDLinkList *)list;DLinkListNode * ret = NULL;int i = 0;if (sList == NULL || pos < 0){return NULL;}if ((sList != NULL) && (0 <= pos) && (pos <= sList->length)){DLinkListNode * current = (DLinkListNode *)sList;DLinkListNode * next = NULL;for (i = 0; i < pos; i++){current = current->next;}ret = current->next;next = ret->next;//步骤一current->next = 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;}//--add//删除某一个元素DLinkListNode * DLinkList_DeleteNode(DLinkList * list, DLinkListNode *node){TDLinkList * sList = (TDLinkList *)list;DLinkListNode * ret = NULL;int i = 0;if (sList != NULL){DLinkListNode * current = (DLinkListNode *)sList;for (i = 0; i < sList->length; i++){if (current->next = node){ret = current->next;break;}}if (ret != NULL){DLinkList_Delete(sList, i);}}return ret;}//重置游标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;}//获取游标当前位置DLinkListNode * DLinkList_Current(DLinkList *list){TDLinkList * sList = (TDLinkList *)list;DLinkListNode * ret = NULL;if (sList != NULL){ret = sList->slider;}return ret;}//游标下移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;}//游标前移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;}

//Test.cpp#include"DLinkList.h"#include<stdio.h>#include<malloc.h>struct Value{DLinkListNode node;int v;};int main(){int i = 0;DLinkList * list = DLinkList_Create();//创建链表struct Value v1, v2, v3, v4,v5;v1.v = 1; v2.v = 2; v3.v = 3; v4.v = 4, v5.v = 5;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++){//获取游标所指元素,然后游标下移struct Value* pv = (struct Value *)DLinkList_Next(list);printf("%d  ", pv->v);}DLinkList_Reset(list);printf("\n\n");DLinkList_Delete(list, DLinkList_Length(list) - 1);DLinkList_Delete(list, 0);for (i = 0; i < DLinkList_Length(list); i++){//获取游标所指元素,然后游标下移struct Value* pv = (struct Value *)DLinkList_Next(list);printf("%d  ", pv->v);}printf("\n\n");DLinkList_Reset(list);DLinkList_Next(list);struct Value* pv = (struct Value *)DLinkList_Current(list);printf("%d  ", pv->v);printf("\n\n");DLinkList_DeleteNode(list, (DLinkListNode*)pv);pv = (struct Value *)DLinkList_Current(list);printf("%d  ", pv->v);printf("\n\n");printf("Length: %d\n", DLinkList_Length(list));DLinkList_Destory(list);return 0;}

5双向链表的优缺点

优点:双向链表在单链表的基础上增加了指向前驱的指针

功能上双向链表可以完全取代单链表的使用

双向链表的Next,Pre和Current操作可以高效的遍历链表中的所有元素

缺点:代码复杂
0 0
原创粉丝点击