C语言|数据结构-双向循环链表

本文详细介绍了在C语言中如何实现双向循环链表,包括结构体定义、链表的创建、销毁、插入、查找、删除以及使用函数指针进行操作。重点展示了在Linux内核源码中的应用实例。
摘要由CSDN通过智能技术生成

C语言|数据结构-双向循环链表

编译系统@CentOS 7 x86_64

gcc version 4.8.5

双向循环链表是一种比较实用的数据结构,在Linux 内核源码里常常使用,因而学习记录一下。

不同于单链表,双向循环链表 可以表示为

在这里插入图片描述

#define GENERAL_CREATE 0
// 1、一般结构体 构造
#if GENERAL_CREATE
//普通节点
struct llist_node_st
{
        void *data;
        llist_st *prev;
        llist_st *next;  
};
#else
//2、变长结构体 构造
struct llist_node_st
{
        struct llist_node_st *prev;
        struct llist_node_st *next;
        char data[0];
     // ps. C99后才支持 0字符数组 当作变长的占位符
        // 也可以写为 char[1] 计算时 需要减去 sizeof(data/data[0])
        // 变长的结构体需要 位于结构体声明最后
};
// 头节点
typedef struct  llist_head
{
        int size;
        struct llist_node_st head;
}LLIST;
#endif

链表的创建

​ 操作:创建一个头结点

//1、一般结构体 创建
/*
*	@brief: 创建头节点
*	@param: int initsize 头节点初始化大小
*   @retvalue: LLIST*
*/
#if GENERAL_CREATE
LLIST *llist_create(int initsize)
{
        LLIST *new;
		// 动态申请内存
        new = malloc(sizeof(*new));
        // 如果申请为空 返回空
    	if(new == NULL)
                return NULL;
    	// 初始化
        new->size = initsize;
        new->head.data= NULL;
        new->head.prev = &new->head;
        new->head.next = &new->head;
        //双向循环链表 只有头,则 自己指向自己
        return new;
}
#else
//2. 变长结构体 创建
LLIST *llist_create(int initsize)
{
        LLIST *new;
        new = malloc(sizeof(*new));
        if(new == NULL)
                return NULL;
        new->size = initsize;
        new->head.prev = &new->head;
        new->head.next = &new->head;
        return new;
}
#endif

链表的 销毁

操作 :默认 链表成功创建 ,并销毁 目标链表

/*
*@brief: 销毁 目标链表
*@param: LLIST *llist 目标链表
*@retvalue: void
*/
void llist_destroy(LLIST *llist) // 默认 成功 创建
{
        struct llist_node_st *cur,*next;
    //ps.头节点的 头部的下次指针,由于 头节点 是循环的 如果 是头节点 则结束循环
        for(cur = llist->head.next; cur != &llist->head; cur = next)
        {
                next = cur->next;
        	    // free(cur->data);
                free(cur);
        }
        free(llist);
}

链表的 插入

操作 :插入模式 INSERT_MODE_FORWARD,INSERT_MODE_REAR

如果要面向对象编程的话 也可以利用函数指针进行 操作

#include<string.h> // memcpy
#include<stdio.h>
#include<stdlib.h>

#define INSERT_MODE_REAR	1
#define INSERT_MODE_FORWARD	2
/*
*@brief: 插入 数据 到 链表
*@param: LLIST *llist 目标链表
*		 const void *data 插入数据
*		 int mode 模式 
*@retvalue: int 状态值
**/
int llist_insert(LLIST *llist, const void *data,int mode) // 由于 循环列表 则只设置 两>种模式 头插 或者 尾插
{
        struct llist_node_st *new_node;
        new_node = malloc(sizeof(*new_node) + llist->size); //如果 要地址对齐,也要需要 减去至少四个字节
        if(new_node == NULL) return -1;
//      new_node->data = malloc(llist->size);
//      if(new_node->data == NULL) return -2;
        memcpy(new_node->data,data,llist->size);
        // void *memcpy(void *dest, const void *src, size_t n);
        if(mode == INSERT_MODE_FORWARD)
        {
                new_node->prev = &llist->head;
                new_node->next = llist->head.next;
        }
        else if(mode == INSERT_MODE_REAR)
        {
                new_node->prev = llist->head.prev;
                new_node->next = &llist->head;
        }
        else
        {
                return -3;
        }
                new_node->prev->next = new_node;
                new_node->next->prev = new_node;
                return 0;
}

图示(头插法)

在这里插入图片描述

链表的 查找

操作:根据关键字查找,可以创建回调函数查找关键字段,为删除和提取做准备

// 类型定义 返回值类型 函数类型名(参数列表); 可以当作回调函数 传入函数指针 来指定 关于关键字段的特定查找和比较方式  
typedef int llist_cmp(const void*,const void*);
// 为删除和提取 封装 重复 操作
static struct llist_node_st* find_(LLIST* ptr, const void* key, llist_cmp* cmp)
{
        struct llist_node_st *cur;
        for(cur = ptr->head.next; cur != &ptr->head ; cur = cur->next)
        {
                if(cmp(key,cur->data) == 0)// 无重复值
                        break;

        }
        return cur;
}
/*
*@brief: 在 目标链表 根据关键字 根据回调函数进行寻找
*@param: LLIST *llist 目标链表
*		 const void *key 关键字
*		 llist_cmp *cmp 回调函数
*@retvalue: void *
**/
void* llist_find(LLIST* llist, const void* key,llist_cmp *cmp)
{
#if GENERAL_CREATE
        return  find_(llist,key,cmp)->data; // 获取 和删除 都要先查找 所以可以例化
#else  
        struct llist_node_st *node;
        node = find_(llist,key,cmp);
    //找到头节点 就 返回空 即 以头节点为存在基础
        if(node == &llist->head)
               return NULL;
        return node->data;
#endif           

链表的 删除 特定元素

操作:根据 关键字 删除 特定 元素

/*
*@brief: 在 目标链表 根据关键字 根据回调函数进行删除
*@param: LLIST *llist 目标链表
*		 const void *key 关键字
*		 llist_cmp *cmp 回调函数
*@retvalue: int 状态值
**/
int llist_delete(LLIST* llist, const void* key,llist_cmp*cmp)
{
        struct llist_node_st *node;
        node = find_(llist,key,cmp);
        if(node == &llist->head)
                return -1;      // 不删除头结点

        node->prev->next = node->next;
        node->next->prev = node->prev;

        ///free(node->data);
        free(node);
        return 0;
}

链表的 寻找 特定元素

操作:根据 关键字 寻找 特定 元素

/*
*@brief: 在 目标链表 根据关键字 根据回调函数进行删除
*@param: LLIST *llist 目标链表
*		 const void *key 关键字
*		 llist_cmp *cmp 回调函数
*		 void *data 所寻找的数据
*@retvalue: int 状态值
**/
int llist_fetch(LLIST* llist, const void* key,llist_cmp* cmp,void* data)
{

        struct llist_node_st *node;
        node = find_(llist,key,cmp);
        if(node == &llist->head)
                return -1;      // 不删除头结点

        node->prev->next = node->next;
        node->next->prev = node->prev;
        if(data != NULL)
                memcpy(data,node->data,llist->size);

        //free(node->data);
        free(node);
        return 0;
}

函数指针的定义和插入

类似类的方法操作,可以用函数指针来完成操作

typedef struct  llist_head
{
        int size;
        struct llist_node_st head;
        int (*insert)(struct llist_head*,const void*,int);
        // 函数指针 RetValueType (Pointer* Force Transfer)(Parameter List);
}LLIST;

则 在创建时就可以

int llist_insert(LLIST *llist, const void *data,int mode); // 由于 循环列表 则只设置 两
种模式 头插 或者 尾插

LLIST *llist_create(int initsize)
{
        LLIST *new;

        new = malloc(sizeof(*new));
        if(new == NULL)
                return NULL;

        new->size = initsize;
//      new->head.data= NULL;
        new->head.prev = &new->head;
        new->head.next = &new->head;
        //!!!
    	new->insert = llist_insert;
        //双向循环链表 只有头,则 自己指向自己
        return new;
}

        if(new == NULL)
                return NULL;

        new->size = initsize;
//      new->head.data= NULL;
        new->head.prev = &new->head;
        new->head.next = &new->head;
        //!!!
    	new->insert = llist_insert;
        //双向循环链表 只有头,则 自己指向自己
        return new;
}

写在最后

加油鼓劲,一切都是最好的安排!

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值