C实现通用数据结构--双向链表

双向链表概述

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别
指向直接后继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 = 0;
    list->destroy = destroy;
    list->head = NULL;
    list->tail = NULL;
    return;
}

回收双向链表:

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

    //向链表中插入元素
    new_element->data = (void *)data;
    if (dlist_size(list) == 0) {
           //当链表为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 0;
}

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

//插入指定元素的前驱
int dlist_ins_prev(DList *list, DListElmt *element, const void *data) {

    DListElmt *new_element;
    if (element == NULL && dlist_size(list) != 0)   //不允许element元素为NULL,除非list为空.
        return -1;
    if ((new_element = (DListElmt *)malloc(sizeof(DListElmt))) == NULL)   //为element分配空间
        return -1;

    //向链表中插入元素
    new_element->data = (void *)data;
    if (dlist_size(list) == 0) {
        //当链表为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 0;
}

删除指定结点:

//删除指定结点
int dlist_remove(DList *list, DListElmt *element, void **data) {

    //不允许删除NULL元素或从空表中删除元素
    if (element == NULL || dlist_size(list) == 0)
        return -1;

    //从表中删除元素
    *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 0;
}
时间: 2024-10-21 20:28:58

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

Linux 内核里的数据结构——双向链表

Linux 内核里的数据结构--双向链表 Linux 内核中自己实现了双向链表,可以在 include/linux/list.h 找到定义.我们将会首先从双向链表数据结构开始介绍内核里的数据结构.为什么?因为它在内核里使用的很广泛,你只需要在 free-electrons.com 检索一下就知道了. 首先让我们看一下在 include/linux/types.h 里的主结构体: struct list_head { struct list_head *next, *prev; }; 你可能注意到

一步一步写算法(之通用数据结构)

原文:一步一步写算法(之通用数据结构) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]     上一篇博客介绍了通用算法,那么有了这个基础我们可以继续分析通用数据结构了.我们知道在c++里面,既有数据又有函数,所以一个class就能干很多事情.举一个简单的例子来说,我们可以编写一个数据的class计算类. class calculate{ int m; int n; public: calculate():m(0),n(0) {} cal

C语言数据结构双向链表之温故而知新

单向链表:http://blog.csdn.net/morixinguan/article/details/77756216 单向链表理解了,那双向就非常简单了,没什么好说的,看图: 双链表的引入是为了解决单链表的不足:(1)双链表可以往前遍历,也可以往后遍历,具有两个方向双链表的节点 = 有效数据 + 两个指针(分别指向前一个节点和后一个节点)双向链表的图形结构描述: 可以用一个简单的数据结构来描述上面的这个图: struct double_list struct double_list {

C实现通用数据结构--单链表

单链表概述 单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始. 从概念上讲,可以把链表想象成一系列连续的元素,然而,由于这些元素是动态分配的(C语言中使用malloc),切记这些元素通常实际上都是分散在内存空间的 欢迎关注我的个人博客:www.wuyudong.com, 更多精彩文章与您分享 单链表的接口定义: 1.list_init void list_init(List *list, void (*destroy)(void *data))

C++实现通用双向链表

使用C++完成双向通用链表 双向链表不用多说,通用链表因为数据结构不确定的,使用一个VOID指针指向数据, 什么数据都可以挂上去,这样来封装链表,可以作为基础类也可以单独使用, 这里只是为了练习C++封装的语法,实现了简单的增加和删除链表由于实际数据 类型不能确定,打印链表数据使用公有函数来完成,完成了正向打印反向打印, 演示了数据类型为简单的int类型也演示了数据类型为class类型. 代码如下: 点击(此处)折叠或打开 链表实现: #include<iostream> #include&l

数据结构和算法03 之链表

在第一章的数组中,我们看到数组作为数据存储结构有一定的缺陷.在无序数组中,搜索时低效的:而在有序数组中,插入效率又很低:不管在哪一种数组中删除效率都很低.况且一个数组创建后,它的大小是无法改变的.         在本章中,我们将讨论下链表这个数据结构,它可以解决上面的一些问题.链表可能是继数组之后第二种使用得最广泛的通用数据结构了.本章主要讨论单链表和双向链表.         顾名思义,单链表只能从表头到表尾的顺序,每个节点中保存了指向下一个节点的指针:双向链表则可以反向遍历,因为节点中既保

数据结构和算法18 之总结篇

  前面介绍了经典的数据结构和算法,这一节我们对这些数据结构和算法做一个总结,具体细节,请参见各个章节的详细介绍,这里我们用表格来呈现它们的效率. 1. 数据结构部分 数据结构中常用的操作的效率表 通用数据结构 查找  插入   删除 遍历  数组 O(N) O(1) O(N) - 有序数组 O(logN) O(N) O(N) O(N) 链表 O(N) O(1) O(N) - 有序链表 O(N) O(N) O(N) O(N) 二叉树 O(logN) O(logN) O(logN) O(N) 二叉

一步一步写算法(之 算法总结)

原文:一步一步写算法(之 算法总结) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]       自10月初编写算法系列的博客以来,陆陆续续以来写了几十篇.按照计划,还有三个部分的内容没有介绍,主要是(Dijkstra算法.二叉平衡树.红黑树).这部分会在后面的博客补充完整.这里主要是做一个总结,有兴趣的朋友可以好好看看,欢迎大家提出宝贵意见.       (1) 排序算法     快速排序           合并排序     堆排序

一步一步写算法(之 算法总结)【转】

转自:http://blog.csdn.net/feixiaoxing/article/details/6993718 版权声明:本文为博主原创文章,未经博主允许不得转载. [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]     自10月初编写算法系列的博客以来,陆陆续续以来写了几十篇.按照计划,还有三个部分的内容没有介绍,主要是(Dijkstra算法.二叉平衡树.红黑树).这部分会在后面的博客补充完整.这里主要是做一个总结,有兴趣的朋友