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

来源:互联网 发布:踪迹谋杀之谜类似知乎 编辑:程序博客网 时间:2024/04/26 21:34

双向链表概述

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