也没想象中那么神秘的数据结构-一种通用化双向链表设计(对象设计)

概述

在操作系统内核中,绝大多数数据结构都是通过链表来实现的,相对于数组,链表可以很方便的用来管理数据,给数据管理带来了更多的可能性。

之前的文章《也没想象中那么神秘的数据结构-一种通用化双向链表设计(底层源码)》中已经对概念和注意等阐述过,这里就不再重复了,有兴趣的小伙伴,可点击上方链接查看具体信息。

注意

1、C语言相比c++、java和python等语言,因为没有类相关的语法支撑,所以一直以来被冠以面向过程语言。

2、实际项目中,特别是大型项目中,我们应该采用面向对象设计而非面向过程设计的方法。

3、面向过程只是一种设计思想,而不应该用语言对其加以局限

4、C语言也可通过结构体、函数指针等语法实现面向对象的设计,这里后期小编会再出一篇文章对此进行阐述和说明。

介绍

本文旨在将通用链表的操作和属性抽象封装成一个类,类的方法对应通用链表的基本操作,后期使用的时候,只需要将类实例化,即可调用相关属性方法,完成链表的一系列操作。

核心

链表类设计:

属性:链表

行为:包括插入节点、查找节点、删除节点、替换节点等,具体见下方代码。

/* 双向链表类 dll = class doubly linked list */
struct dll {   
    struct t_list *p_list;   /* 链表 */

    void (*print_node)(struct t_node *node)                     /* 打印节点信息 */
    int (*key_in_node)(struct t_node *node, void *key)          /* 判断节点中是否包含指定信息 */   
    void (*add)(struct dll *p_dll, struct t_node *p_node);      /* 添加节点到链表尾部 */
    void (*insert_pos)(struct dll *p_dll, 
                int pos, struct t_node *node);                  /* 在链表指定位置插入节点 */
    struct t_node *(*get_first)(struct dll *p_dll);             /* 获取链表头结点,不删除 */
    struct t_node *(*get)(struct dll *p_dll);                   /* 获取链表头结点并删除 */
    void (*insert)(struct dll *p_dll, 
        struct t_node *p_prev, struct t_node *p_node);          /* 在链表指定节点之后插入节点 */
    struct t_node *(*get_last)(struct dll *p_dll);              /* 获取链表尾结点,不删除 */
    struct t_node *(*get_next)(struct t_node *p_node);          /* 获取下一个结点,不删除 */
    struct t_node *(*get_n_th)(struct dll *p_dll, int pos);     /* 获取第N个结点,不删除 */
    struct t_node *(*get_prev)(struct t_node *p_node);          /* 获取前一个结点,不删除 */   
    struct t_node *(*rm_n_th)(struct dll *p_dll, int pos);      /* 移除链表中第N个结点 */
    void (*del_n_th)(struct dll *p_dll, int pos);               /* 删除链表中第N个结点 */
    void (*replace)(struct dll *p_dll, 
                int pos, struct t_node *node);                  /* 替换链表指定位置节点 */
    int (*get_count)(struct dll *p_dll);                        /* 链表长度获取 */
    void (*rm)(struct dll *p_dll, struct t_node *p_node);       /* 移除链表中指定节点 */
    void (*del)(struct dll *p_dll, struct t_node *p_node);      /* 删除链表中指定节点 */   
    struct t_node *(*get_n_step)(struct t_node *p_node, int n); /* 获取距离指定节点N步的结点 */
    int (*find)(struct dll *p_dll, struct t_node *p_node);      /* 查找指定节点在链表中的位置 */
    struct t_node * (*find_key)(struct dll *p_dll, void *key);  /* 根据指定信息查找节点 */   
    struct t_node *(*rm_key)(struct dll *p_dll, void *key);     /* 移除链表中指定信息的节点 */
    void (*del_key)(struct dll *p_dll, void *key);              /* 删除链表中指定信息的节点 */ 
    void (*show)(struct dll *p_dll);                            /* 打印链表信息 */   
    void (*destory)(struct dll *p_dll);                         /* 销毁释放链表 */
    void (*concat)(struct dll *p_dll, struct t_list *p_add);    /* 拼接两个链表 */
    void (*extract)(struct dll *p_dll, struct t_node *p_start, 
            struct t_node *p_end, struct t_list *p_dst);        /* 提取链表中的子链 */
};

在指定位置插入节点:

1. 找到指定位置。

2. 插入节点。

/**
 * @在链表指定位置插入节点
 * @p_dll:链表类       pos:指定位置        p_node:插入节点
**/
static void _insert_pos(struct dll *p_dll, int pos, struct t_node *node)
{
    struct t_node *p_node = NULL;

    if ((p_dll == NULL) || (p_node == NULL))
        return;

    if ((p_node = lst_n_th(p_dll->p_list, pos)) == NULL)    /* 获取第N个结点 */
        return;

    lst_insert(p_dll->p_list, lst_prev(p_node), p_node);
}

替换链表中的指定位置节点:

1. 找到指定位置节点。

2. 删除旧节点。

3. 插入新节点。

4. 销毁旧节点。

/**
 * @替换链表中第n个节点为指定节点
 * @p_dll:链表类       pos:指定位置        p_node:插入节点
**/
static void _replace(struct dll *p_dll, int pos, struct t_node *node)
{
    struct t_node *p_node = NULL;

    if ((p_dll == NULL) || (p_node == NULL))
        return;

    if ((p_node = lst_n_th(p_dll->p_list, pos)) == NULL)     /* 获取第N个结点 */
        return;

    lst_del(p_dll->p_list, p_node);
    lst_insert(p_dll->p_list, lst_prev(p_node), p_node);  
    free(p_node);
}

根据指定信息查找节点:

1. 遍历整个链表。

2. 对比各节点信息与指定信息是否一致。

/**
 * @根据指定信息查找节点
 * @p_dll:链表类       key:指定信息 
 * @找到返回结点,否则返回NULL
**/
static struct t_node * _find_key(struct dll *p_dll, void *key)
{
    struct t_node *p_node = NULL;

    if ((p_dll == NULL) || (key == NULL))
        return NULL;

    if (p_dll->key_in_node == NULL)
        return NULL;

    p_node = lst_first(p_dll->p_list);
    while (p_node != NULL) {
        if (p_dll->key_in_node(p_node, key) == 0)
            return p_node;
        p_node = lst_next(p_node);  
    } 
    return NULL;
}

删除指定信息节点:

1. 找到指定信息节点。

2. 删除并销毁节点。

/**
 * @删除链表中指定信息的节点并释放内存
 * @p_dll:链表类       key:指定信息 
**/
static void _del_key(struct dll *p_dll, void *key)
{
    struct t_node *p_node = _find_key(p_dll, key);
    if (p_node == NULL)
        return;

    lst_del(p_dll->p_list, p_node);
    free(p_node);
}

输出整个链表信息:

1. 遍历整个链表。

2. 调用输出函数(这里用到了设计模式中的模板模式,感兴趣的读者可查看文章《我用C语言玩对象,框架化的模板模式》),输出所有节点信息。

/**
 * @打印输出链表信息
 * @p_dll:链表类
**/
static void _show(struct dll *p_dll)
{
    struct t_node *p_node = NULL;

    if (p_dll == NULL)
        return;

    if (p_dll->print_node == NULL)
        return;

    p_node = lst_first(p_dll->p_list);
    while (p_node != NULL) {
        p_dll->print_node(p_node);
        p_node = lst_next(p_node);  
    } 
    printf("\n");
}

示例

★包含头文件class_dll.h和源文件class_dll.c(均已验证通过)。

 class_dll.h

/**
 * @Filename : class_dll.h
 * @Revision : $Revision: 1.0 $
 * @Author : Feng(更多编程相关的知识和源码见微信公众号:不只会拍照的程序猿,欢迎订阅)
 * @Description : 通用双向链表类实现
 * @Explain : 将双向链表抽象成类,实现二次封装,提高通用性
**/

#ifndef __CLASS_DLL_H__
#define __CLASS_DLL_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

struct t_list;      /* 链表定义 */
struct t_node;      /* 节点定义 */

/* 判断节点中是否包含指定信息 */
typedef int (*p_cmp_key_func)(struct t_node *node, void *key);

/* 打印节点信息 */
typedef void (*p_print_node_func)(struct t_node *node);                  

/* 双向链表类 dll = class doubly linked list */
struct dll {   
    struct t_list *p_list;   /* 链表 */

    void (*print_node)(struct t_node *node)                     /* 打印节点信息 */
    int (*key_in_node)(struct t_node *node, void *key)          /* 判断节点中是否包含指定信息 */   
    void (*add)(struct dll *p_dll, struct t_node *p_node);      /* 添加节点到链表尾部 */
    void (*insert_pos)(struct dll *p_dll, 
                int pos, struct t_node *node);                  /* 在链表指定位置插入节点 */
    struct t_node *(*get_first)(struct dll *p_dll);             /* 获取链表头结点,不删除 */
    struct t_node *(*get)(struct dll *p_dll);                   /* 获取链表头结点并删除 */
    void (*insert)(struct dll *p_dll, 
        struct t_node *p_prev, struct t_node *p_node);          /* 在链表指定节点之后插入节点 */
    struct t_node *(*get_last)(struct dll *p_dll);              /* 获取链表尾结点,不删除 */
    struct t_node *(*get_next)(struct t_node *p_node);          /* 获取下一个结点,不删除 */
    struct t_node *(*get_n_th)(struct dll *p_dll, int pos);     /* 获取第N个结点,不删除 */
    struct t_node *(*get_prev)(struct t_node *p_node);          /* 获取前一个结点,不删除 */   
    struct t_node *(*rm_n_th)(struct dll *p_dll, int pos);      /* 移除链表中第N个结点 */
    void (*del_n_th)(struct dll *p_dll, int pos);               /* 删除链表中第N个结点 */
    void (*replace)(struct dll *p_dll, 
                int pos, struct t_node *node);                  /* 替换链表指定位置节点 */
    int (*get_count)(struct dll *p_dll);                        /* 链表长度获取 */
    void (*rm)(struct dll *p_dll, struct t_node *p_node);       /* 移除链表中指定节点 */
    void (*del)(struct dll *p_dll, struct t_node *p_node);      /* 删除链表中指定节点 */   
    struct t_node *(*get_n_step)(struct t_node *p_node, int n); /* 获取距离指定节点N步的结点 */
    int (*find)(struct dll *p_dll, struct t_node *p_node);      /* 查找指定节点在链表中的位置 */
    struct t_node * (*find_key)(struct dll *p_dll, void *key);  /* 根据指定信息查找节点 */   
    struct t_node *(*rm_key)(struct dll *p_dll, void *key);     /* 移除链表中指定信息的节点 */
    void (*del_key)(struct dll *p_dll, void *key);              /* 删除链表中指定信息的节点 */ 
    void (*show)(struct dll *p_dll);                            /* 打印链表信息 */   
    void (*destory)(struct dll *p_dll);                         /* 销毁释放链表 */
    void (*concat)(struct dll *p_dll, struct t_list *p_add);    /* 拼接两个链表 */
    void (*extract)(struct dll *p_dll, struct t_node *p_start, 
            struct t_node *p_end, struct t_list *p_dst);        /* 提取链表中的子链 */
};

/**
 * @创建DLL类
 * @_cmp_key:信息比较函数   _print_node:打印节点信息函数
 * @成功返回类地址,失败返回NULL       
**/
struct dll *new_dll(p_cmp_key_func _cmp_key, p_print_node_func _print_node);

#ifdef __cplusplus
}
#endif

#endif /* __INClstLibh */

 class_dll.c

/**
 * @Filename : class_dll.c
 * @Revision : $Revision: 1.0 $
 * @Author : Feng(更多编程相关的知识和源码见微信公众号:不只会拍照的程序猿,欢迎订阅)
 * @Description : 通用双向链表类实现
 * @Explain : 将双向链表抽象成类,实现二次封装,提高通用性
**/

#include "dll.h"
#include "class_dll.h"

/**
 * @添加节点到链表尾部
 * @p_dll:链表类       p_node:插入节点
**/
static void _add(struct dll *p_dll, struct t_node *p_node)
{
    if ((p_dll == NULL) || (p_node == NULL))
        return;

    lst_add(p_dll->p_list, p_node);
}

/**
 * @在链表指定位置插入节点
 * @p_dll:链表类       pos:指定位置        p_node:插入节点
**/
static void _insert_pos(struct dll *p_dll, int pos, struct t_node *node)
{
    struct t_node *p_node = NULL;

    if ((p_dll == NULL) || (p_node == NULL))
        return;

    if ((p_node = lst_n_th(p_dll->p_list, pos)) == NULL)    /* 获取第N个结点 */
        return;

    lst_insert(p_dll->p_list, lst_prev(p_node), p_node);
}

/**
 * @获取链表头结点,不删除
 * @p_dll:链表类
 * @返回头结点
**/
struct t_node *_get_first(struct dll *p_dll)
{
    return (lst_first(p_dll->p_list));
}

/**
 * @获取链表头结点并删除
 * @p_dll:链表类
 * @返回头结点
**/
static struct t_node *_get(struct dll *p_dll)
{
    return (lst_get(p_dll->p_list));
}

/**
 * @在链表指定节点之后插入节点
 * @p_dll:链表类       p_prev:指定节点     p_node:插入节点
**/ 
static void _insert(struct dll *p_dll, 
                    struct t_node *p_prev, struct t_node *p_node)
{                 
    lst_insert(p_dll->p_list, p_prev, p_node);
}

/**
 * @获取链表尾结点,不删除
 * @p_dll:链表类
 * @返回尾结点
**/ 
static struct t_node *_get_last(struct dll *p_dll)
{
    return (lst_last(p_dll->p_list));
}

/**
 * @获取下一个结点,不删除
 * @p_node:节点
 * @返回下一个结点
**/ 
static struct t_node *_get_next(struct t_node *p_node)
{
    return (lst_next(p_node));
}

/**
 * @获取第N个结点,不删除
 * @p_dll:链表类       p_node:节点
 * @返回结点 
**/ 
static struct t_node *_get_n_th(struct dll *p_dll, int pos)
{
    return (lst_n_th(p_dll->p_list, pos));
}

/**
 * @获取前一个结点,不删除
 * @p_node:节点
 * @返回前一个结点 
**/       
static struct t_node *_get_prev(struct t_node *p_node)
{
    return (lst_prev(p_node));
}

/**
 * @移除链表中第N个结点不释放内存
 * @p_dll:链表类       pos:节点偏移量
 * @成功返回结点,否则返回NULL 
**/
static struct t_node * _rm_n_th(struct dll *p_dll, int pos)
{
    struct t_node *p_node = lst_n_th(p_dll->p_list, pos);

    lst_del(p_dll->p_list, p_node);
    return(p_node);
}


/**
 * @删除链表中第N个结点释放内存
 * @p_dll:链表类       pos:节点偏移量
**/
static void _del_n_th(struct dll *p_dll, int pos)
{
    struct t_node *p_node = lst_n_th(p_dll->p_list, pos);

    lst_del(p_dll->p_list, p_node);
    free(p_node);
}

/**
 * @替换链表中第n个节点为指定节点
 * @p_dll:链表类       pos:指定位置        p_node:插入节点
**/
static void _replace(struct dll *p_dll, int pos, struct t_node *node)
{
    struct t_node *p_node = NULL;

    if ((p_dll == NULL) || (p_node == NULL))
        return;

    if ((p_node = lst_n_th(p_dll->p_list, pos)) == NULL)     /* 获取第N个结点 */
        return;

    lst_del(p_dll->p_list, p_node);
    lst_insert(p_dll->p_list, lst_prev(p_node), p_node);  
    free(p_node);
}

/**
 * @获取链表长度
 * @p_dll:链表类        
 * @返回链表长度
**/
static int _get_count(struct dll *p_dll)
{
    return (lst_count(p_dll->p_list));
} 

/**
 * @移除链表中指定节点,未释放内存
 * @p_dll:链表类        p_node:指定节点
**/
static void _rm(struct dll *p_dll, struct t_node *p_node)
{
    lst_del(p_dll->p_list, p_node);
}

/**
 * @删除链表中指定节点并释放内存
 * @p_dll:链表类        p_node:指定节点
**/
static void _del(struct dll *p_dll, struct t_node *p_node)
{
    lst_del(p_dll->p_list, p_node);
    free(p_node);
}

/**
 * @获取距离指定节点N步的结点,不删除
 * @n:距离,>0在后面,<0在前面        p_node:指定节点
 * @返回结点 
**/
static struct t_node *_get_n_step(struct t_node *p_node, int n)
{
    return (lst_n_step(p_node, n));
}

/**
 * @查找指定节点在链表中的位置
 * @p_dll:链表类       p_node:指定节点
 * @返回位置(从1开始,不存在返回-1)
**/
static int _find(struct dll *p_dll, struct t_node *p_node)
{
    return (lst_find(p_dll->p_list, p_node));
}

/**
 * @根据指定信息查找节点
 * @p_dll:链表类       key:指定信息 
 * @找到返回结点,否则返回NULL
**/
static struct t_node * _find_key(struct dll *p_dll, void *key)
{
    struct t_node *p_node = NULL;

    if ((p_dll == NULL) || (key == NULL))
        return NULL;

    if (p_dll->key_in_node == NULL)
        return NULL;

    p_node = lst_first(p_dll->p_list);
    while (p_node != NULL) {
        if (p_dll->key_in_node(p_node, key) == 0)
            return p_node;
        p_node = lst_next(p_node);  
    } 
    return NULL;
}

/**
 * @移除链表中指定信息的节点,未释放内存
 * @p_dll:链表类       key:指定信息 
 * @成功返回包含信息结点,否则返回NULL
**/
static struct t_node * _rm_key(struct dll *p_dll, void *key)
{
    struct t_node *p_node = _find_key(p_dll, key);
    if (p_node == NULL)
        return NULL;

    lst_del(p_dll->p_list, p_node);
    return (p_node);
}

/**
 * @删除链表中指定信息的节点并释放内存
 * @p_dll:链表类       key:指定信息 
**/
static void _del_key(struct dll *p_dll, void *key)
{
    struct t_node *p_node = _find_key(p_dll, key);
    if (p_node == NULL)
        return;

    lst_del(p_dll->p_list, p_node);
    free(p_node);
}

/**
 * @打印输出链表信息
 * @p_dll:链表类
**/
static void _show(struct dll *p_dll)
{
    struct t_node *p_node = NULL;

    if (p_dll == NULL)
        return;

    if (p_dll->print_node == NULL)
        return;

    p_node = lst_first(p_dll->p_list);
    while (p_node != NULL) {
        p_dll->print_node(p_node);
        p_node = lst_next(p_node);  
    } 
    printf("\n");
}

/**
 * @销毁释放链表
 * @p_dll:链表类
**/
static void _destory(struct dll *p_dll)
{
    lst_free(p_dll->p_list);
}

/**
 * @拼接两个链表
 * @p_dll:链表类     p_add:添加链表
**/
static void _concat(struct dll *p_dll, struct t_list *p_add)
{
    lst_concat(p_dll->p_list, p_add);
}

/**
 * @提取链表中的子链
 * @p_dll:链表类     p_start:起始节点      p_end:终止节点      p_dst:子链
**/
static void _extract(struct dll *p_dll, struct t_node *p_start,
                        struct t_node *p_end, struct t_list *p_dst)
{
    lst_extract(p_dll->p_list, p_start, p_end, p_dst);
}

/**
 * @创建DLL类
 * @_cmp_key:信息比较函数   _print_node:打印节点信息函数
 * @成功返回类地址,失败返回NULL       
**/
struct dll *new_dll(p_cmp_key_func _cmp_key, p_print_node_func _print_node)
{
    struct t_list *p_list = NULL;
    struct dll *p_dll = (struct dll *)malloc(sizeof(struct dll));

    if (p_dll == NULL)
        return NULL;

    if ((p_list = (struct t_list *)malloc(sizeof(struct t_list))) == NULL) {
        free(p_dll);
        return NULL;
    }

    memset(p_dll, 0, sizeof(struct dll));

    lst_init(p_list);
    p_dll->p_list = p_list;

    p_dll->key_in_node = _cmp_key;
    p_dll->print_node = _print_node;
    p_dll->add = _add;
    p_dll->insert_pos = _insert_pos;
    p_dll->get_first = _get_first;
    p_dll->get = _get;
    p_dll->insert = _insert;
    p_dll->get_last = _get_last;
    p_dll->get_next = _get_next;
    p_dll->get_n_th = _get_n_th;
    p_dll->get_prev = _get_prev;
    p_dll->del_n_th = _del_n_th;
    p_dll->replace = _replace;
    p_dll->get_count = _get_count;
    p_dll->rm = _rm;
    p_dll->del = _del;
    p_dll->get_n_step = _get_n_step;    
    p_dll->find = _find;
    p_dll->find_key = _find_key;    
    p_dll->rm_key = _rm_key;
    p_dll->del_key = _del_key;
    p_dll->show = _show;
    p_dll->destory = _destory;
    p_dll->concat = _concat;
    p_dll->extract = _extract;   

    return p_dll;
}

结论

示例代码将文章《也没想象中那么神秘的数据结构-一种通用化双向链表设计(底层源码)》中源码进行二次封装,抽象出属性和通用性方法设计通用类,后期读者在使用的时候,只需要对其进行实例化,调用其中的方法即可。同时,公众号内文章《也没想象中那么神秘的数据结构-一种通用化双向链表设计(测试例程)》也会向读者展示具体使用方法。

往期 · 推荐

也没想象中那么神秘的数据结构-一种通用化的双向链表设计(底层源码)

也没想象中那么神秘的数据结构-一环扣一环的“链表”(双向链表)

我用C语言玩对象,框架化的模板模式

我用C语言玩对象,偷偷关注着你的观察者模式(基类设计)

我用C语言玩对象,独一无二的单例模式

关注

更多精彩内容,请关注微信公众号:不只会拍照的程序猿,本人致力分享linux、设计模式、C语言、嵌入式、编程相关知识,也会抽空分享些摄影相关内容,同样也分享大量摄影、编程相关视频和源码,另外你若想要本文章源码请关注公众号:不只会拍照的程序猿,后台回复:数据结构源码,也可点击此处下载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不只会拍照的程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值