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;
}
写在最后
加油鼓劲,一切都是最好的安排!