• 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;
    }
  • 相关阅读:
    day 48
    40 协程 多路复用
    JQuery
    JS DOMBOM
    psotgres、timescaledb
    crontab命令
    Go语言结构体和方法
    Go语言锁的使用
    Go语言map数据结构
    ZOJ 3777 Problem Arrangement
  • 原文地址:https://www.cnblogs.com/wuyudong/p/c-datastruct-dlinklist.html
Copyright © 2020-2023  润新知