数据结构之线性表——双向链表
来源:互联网 发布: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操作可以高效的遍历链表中的所有元素
缺点:代码复杂- 数据结构之线性表——双向链表
- 数据结构之线性结构--双向链表
- 数据结构之—线性表之—双向链表之—浅谈双循环链表
- JAVA数据结构之线性表的链式存储结构——双向链表
- 数据结构与算法专题之线性表——链表(二)双向链表
- 数据结构学习笔记(4.线性表之双向链表)
- 数据结构学习笔记(4.线性表之双向链表)
- Java数据结构(四):线性表之双向链表
- Java数据结构-线性表之双向链表
- 数据结构(10)线性表之双向链表
- 数据结构之线性结构--双向循环链表
- 数据结构-线性表-双向链表
- 数据结构----线性表----双向链表
- 数据结构--线性表--双向链表
- 数据结构与算法-----双向线性链表
- [SDUT](2053)数据结构实验之链表九:双向链表 ---双向链表(线性表)
- 大话数据-—线性表之循环/双向链表
- 小猪的数据结构辅助教程——2.7 线性表中的双向循环链表
- 函数指针和函数指针数组
- QML中的JavaScript用法详解(一)-----在qml中将字符串类型数据转换为整型数据
- 表空间的属性+修改属性+查询所有表空间名称,状态
- Angular4的输入属性
- SQL索引优化
- 数据结构之线性表——双向链表
- 非常全面的 Android Bitmap 知识点梳理
- yum Error: rpmdb open failed
- iOS 添加 UA
- EA&UML日拱一卒--序列图(Sequence Diagram)::时间约束
- 来看看 random_state 这个参数
- Linux常用命令(三)
- jquery-QRCode生成二维码
- Operators in MXNet-FullyConnected