双向链表概述

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继next和直接前驱prev。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。为了标识链表的头和尾,将第一个元素的prev指针和最后一个元素的next指针设置为NULL

要反向遍历整个双向链表,使用prev指针从尾到头的顺序访问各个元素,因此为每个元素增加了一个指针的代价,换来的是双向链表更加灵活的访问。

本文地址:http://www.cnblogs.com/archimedes/p/c-datastruct-dlinklist.html,转载请注明源地址。

双向链表接口的定义

1、dlist_init

void dlist_init(DList *list, void (*destroy)(void *data));

描述:初始化由list指定的双向链表,该操作应该在其他操作之前进行。当调用dlist_destory时,这里传入的参数提供了一种释放动态分配空间的方法

复杂度:O(n)

2、dlist_destroy

void dlist_destroy(DList *list);

描述:销毁由list指定的双向链表,该操作之后其他操作不能进行。除非重新调用dlist_init

复杂度:O(n)

3、dlist_ins_next

int dlist_ins_next(DList *list, DListElmt *element, const void *data);

描述:将元素插入到由list指定的双链表中element元素之后,当链表为空的时候,element为NULL,新的元素包含一个指向data的指针,如果插入成功返回1,否则返回-1

复杂度:O(1)

4、dlist_ins_prev

int dlist_ins_prev(DList *list, DListElmt *element, const void *data);

描述:将元素插入到由list指定的双链表中element元素的前面,当链表为空的时候,element为NULL,新的元素包含一个指向data的指针,如果插入成功返回0,否则返回-1

复杂度:O(1)

5、dlist_remove

int dlist_remove(DList *list, DListElmt *element, void **data);

描述:移除由list指定的双链表中element元素,移除操作成功返回0,否则返回-1

复杂度:O(1)

6、dlist_size

int dlist_size(const DList *list);

描述:这是一个宏,用来计算双链表中元素的个数

复杂度:O(1)

7、dlist_head

DListElmt *dlist_head(const DList *list);

描述:这是一个宏,用来返回由list指定的双链表的头结点

复杂度:O(1)

8、dlist_tail

DListElmt dlist_tail(const DList *list);

描述:这是一个宏,用来返回由list指定的双链表的尾结点

复杂度:O(1)

9、dlist_is_head

int dlist_is_head(const DListElmt *element);

描述:这是一个宏,用来判断由element元素指定的元素是否为头结点,如果是返回1,否则返回0

复杂度:O(1)

10、dlist_is_tail

int dlist_is_tail(const DListElmt *element);

描述:这是一个宏,用来判断由element元素指定的元素是否为尾结点,如果是返回0,否则返回-1

复杂度:O(1)

11、dlist_data

void *dlist_data(const DListElmt *element);

描述:这是一个宏,用来返回由element元素指定的元素的数据域

复杂度:O(1)

12、dlist_next

DListElemt *dlist_next(const DListElmt *element);

描述:这是一个宏,用来返回由element元素指定的元素的后继结点,如果是返回0,否则返回-1

复杂度:O(1)

13、dlist_prev

DListElemt *dlist_prev(const DListElmt *element);

描述:这是一个宏,用来返回由element元素指定的元素的前驱结点,如果是返回0,否则返回-1

复杂度:O(1)

双向链表的实现和分析

抽象数据类型的头文件(list.h):

typedef struct DListElmt_ {  //为双链表结点建立结构

    void               *data;   //指向结点的数据域
struct DListElmt_ *prev; //指向结点的前驱结点
struct DListElmt_ *next; //指向结点的前驱结点
} DListElmt; typedef struct DList_ { //建立双链表结构 int size; //元素个数
int (*match)(const void *key1, const void *key2); 匹配函数
void (*destroy)(void *data); 析构函数 DListElmt *head; //指向头结点
DListElmt *tail; //指向尾结点
} DList; //公共接口 void dlist_init(DList *list, void (*destroy)(void *data)); void dlist_destroy(DList *list); int dlist_ins_next(DList *list, DListElmt *element, const void *data); int dlist_ins_prev(DList *list, DListElmt *element, const void *data); int dlist_remove(DList *list, DListElmt *element, void **data); #define dlist_size(list) ((list)->size) #define dlist_head(list) ((list)->head) #define dlist_tail(list) ((list)->tail) #define dlist_is_head(element) ((element)->prev == NULL ? 1 : 0) #define dlist_is_tail(element) ((element)->next == NULL ? 1 : 0) #define dlist_data(element) ((element)->data) #define dlist_next(element) ((element)->next) #define dlist_prev(element) ((element)->prev) #endif

初始化双向链表:

void dlist_init(DList *list, void (*destroy)(void *data)) {  //初始化list
list->size = ;
list->destroy = destroy;
list->head = NULL;
list->tail = NULL;
return;
}

回收双向链表:

void dlist_destroy(DList *list) {
void *data;
//移除每个元素
while (dlist_size(list) > ) {
if (dlist_remove(list, dlist_tail(list), (void **)&data) == && list->destroy != NULL) {
//调用一个用户自定义的函数释放动态分配的内存
list->destroy(data);
}
}
//现在没有操作了,释放结构体作为预防措施
memset(list, , sizeof(DList));
return;
}

插入新节点作为指定结点的直接后继结点:

参考如下示意图:

//插入指定元素的后继
int dlist_ins_next(DList *list, DListElmt *element, const void *data) {
DListElmt *new_element;
//不允许element元素为NULL,除非list为空.
if (element == NULL && dlist_size(list) != )
return -;
//为element分配空间
if ((new_element = (DListElmt *)malloc(sizeof(DListElmt))) == NULL)
return -; //向链表中插入元素
new_element->data = (void *)data;
if (dlist_size(list) == ) {
//当链表为NULL的时候,插入到头结点
list->head = new_element;
list->head->prev = NULL;
list->head->next = NULL;
list->tail = new_element;
} else {
//当链表非空的时候
new_element->next = element->next;
new_element->prev = element;
if (element->next == NULL)
list->tail = new_element;
else
element->next->prev = new_element;
element->next = new_element;
}
//调整链表长度
list->size++;
return ;
}

插入新节点作为指定结点的直接前驱结点:

//插入指定元素的前驱
int dlist_ins_prev(DList *list, DListElmt *element, const void *data) { DListElmt *new_element;
if (element == NULL && dlist_size(list) != ) //不允许element元素为NULL,除非list为空.
return -;
if ((new_element = (DListElmt *)malloc(sizeof(DListElmt))) == NULL) //为element分配空间
return -; //向链表中插入元素
new_element->data = (void *)data;
if (dlist_size(list) == ) {
//当链表为NULL的时候,插入到头结点
list->head = new_element;
list->head->prev = NULL;
list->head->next = NULL;
list->tail = new_element; } else {
//当链表非空的时候插入
new_element->next = element;
new_element->prev = element->prev;
if (element->prev == NULL)
list->head = new_element;
else
element->prev->next = new_element;
element->prev = new_element;
}
//调整链表长度
list->size++;
return ;
}

删除指定结点:

//删除指定结点
int dlist_remove(DList *list, DListElmt *element, void **data) { //不允许删除NULL元素或从空表中删除元素
if (element == NULL || dlist_size(list) == )
return -; //从表中删除元素
*data = element->data; if (element == list->head) {
//删除表头结点
list->head = element->next;
if (list->head == NULL) //如果element元素是尾结点
list->tail = NULL;
else
element->next->prev = NULL;
} else { //删除表中的结点
element->prev->next = element->next;
if (element->next == NULL)
list->tail = element->prev;
else
element->next->prev = element->prev;
}
//释放已经分配的结点
free(element);
//调整表长
list->size--;
return ;
}

C实现通用数据结构--双向链表的更多相关文章

  1. C语言实现通用数据结构的高效设计

    近期在阅读一个开源的C++代码.里面用到了大量的STL里面的东西.或许是自己一直用C而非常少用C++来实现算法的原因.STL里面大量的模板令人心烦.一直对STL的效率表示怀疑,但在网上搜到这样一个帖子 ...

  2. GDSL 1.7 发布,C语言通用数据结构库

    GDSL 1.7 修复了 interval-heap 模块的一个小 bug. GDSL (通用数据结构库) 包含一组程序用于操作各种数据结构.这是一个可移植的库,完全由 ANSI C 编写.为 C 开 ...

  3. (js描述的)数据结构[双向链表](5)

    (js描述的)数据结构[双向链表](5) 一.单向链表的缺点 1.只能按顺序查找,即从上一个到下一个,不能反过来. 二.双向链表的优点 1.可以双向查找 三.双向链表的缺点 1.结构较单向链表复杂. ...

  4. Linux内核分析--内核中的数据结构双向链表【转】

    本文转自:http://blog.csdn.net/yusiguyuan/article/details/19840065 一.首先介绍内核中链表 内核中定义的链表是双向链表,在上篇文章--libev ...

  5. Python数据结构--双向链表

    ''' 双向链表包含第一个和最后一个的链接元素. 每个链接都有一个数据字段和两个称为next和prev的链接字段. 每个链接都使用其下一个链接与其下一个链接链接. 每个链接都使用其上一个链接与之前的链 ...

  6. 数据结构-双向链表(Python实现)

    数据结构在编程世界中一直是非常重要的一环,不管是开发还是算法,哪怕是单纯为了面试,数据结构都是必修课,今天我们介绍链表中的一种--双向链表的代码实现. 好了,话不多说直接上代码. 双向链表 首先,我们 ...

  7. Linux 通用数据结构说明

    device_driver include/linux/device.h struct device_driver { const char             * name; /* 驱动名称 * ...

  8. 学习Redis你必须了解的数据结构——双向链表(JavaScript实现)

    本文版权归博客园和作者吴双本人共同所有,转载和爬虫请注明原文链接 http://www.cnblogs.com/tdws/ 下午分享了JavaScript实现单向链表,晚上就来补充下双向链表吧.对链表 ...

  9. Java数据结构——双向链表

    //================================================= // File Name : DoublyLinked_demo //------------- ...

随机推荐

  1. mysql安装出现error Nr.1045 (转)

    http://www.cnblogs.com/Ivan-j2ee/archive/2012/09/22/2698278.html 我们在windows下安装mysql时会出现Access denied ...

  2. imagepng或imagejpeg浏览器无显示问题

    可以先参考这篇文章,检查一下php的文件编码是否有bom 然而我并不是这个问题,后来想到或许是输出缓冲中有其它内容, 于是尝试 ob_clean();$imagepng($im);或//imagejp ...

  3. 二叉搜索树BinarySearchTree(C实现)

    头文件—————————————————————————————— #ifndef _BINARY_SEARCH_TREE_H_ #define _BINARY_SEARCH_TREE_H_ #inc ...

  4. 编写高质量JS代码的68个有效方法(三)

    [20141030]编写高质量JS代码的68个有效方法(三) *:first-child { margin-top: 0 !important; } body>*:last-child { ma ...

  5. bootstrap插件学习-bootstrap.tab.js

    先看bootstrap-tab.js的结构 var Tab = function ( element ) {} //构造器 Tab.prototype ={} //构造器的原型 $.fn.tab = ...

  6. [OpenCV] Image Processing - Grayscale Transform

    "每个像素的输出值只取决于其输入值" 重难点:Histogram equalization 参考:笑得很甜 http://blog.csdn.net/xiaowei_cqu/art ...

  7. 使用Html5+C#+微信 开发移动端游戏详细教程 :(一)序(关于作者创业失败的感想)

    说起梦想,我清楚的记得2012年7月初毕业,拿到毕业证书的那天果断买好了次日南下去深圳的绿皮火车票,500多块,26个小时车程.第二天就拖上行李到了深圳. 一开始的想法仅仅是过去想见见世面,学习点新技 ...

  8. 20款优秀的国外 Mobile App 界面设计案例

    在下面给大家分享的移动应用程序界面设计作品中,你可以看到不同创意类型的视觉效果.如果你想获得灵感,那很有必要看看下面20个优秀用户体验的移动应用 UI 设计.想要获取更多的灵感,可以访问移动开发分类, ...

  9. Mysql学习笔记(六)增删改查

    PS:数据库最基本的操作就是增删改查了... 学习内容: 数据库的增删改查 1.增...其实就是向数据库中插入数据.. 插入语句 insert into table_name values(" ...

  10. UNIQUEIDENTIFIER列上的统计信息

    UNIQUEIDENTIFIER列上的统计信息非常有意思,在它上面有一些很令人讨厌的行为.我们来看下. 问题重现(The repro) 为了向你展示我们刚抱怨的行为,我用下列简单的表定义创建了一个数据 ...