前言:
循环链表不同于单向链表和双向链表,它比起前两者具有更高的灵活性,和单向链表和双向链表不同,循环链表不用担心遍历到链表的尾巴而无法进行遍历下去。循环链表的最后一个元素不是指向NULL而是指向头部,这样的逻辑结构使得循环链表的每一个元素既可以看为头结点也可以看为尾结点,下图为循环链表的主要两种类型。
双向链表接口的公共接口
void clist_init(DList*list,void (*destroy)(void *data));
返回值:无
描述:这个是循环链表初始化函数,必须在链表进行其他操作其使用,CList*list
是双向链表的头尾信息结构体。当调用list_destroy
时,destroy
参数提供一种释放动态内存的方法。当链表中的元素有动态分配的内存,在摧毁链表时必须free
掉分配的动态内存。destroy
作为一个用户自己可以设置的析构函数,提高了链表的稳定性,如果链表当中不存在应该释放的动态内存,destroy
的值应该为NULL
。
时间复杂度:O(1) 在链表初始化时,时间是固定的。
void clist_destroy(CList* list);
返回值:无
描述:销毁list
指向的双向链表,在销毁后不可以对list
进行任何的数组操作,除非重新在对数组进行初始化操作,如果传给dlist_init
函数的形参destroy
不是NULL
的话,则每次移除链表元素都要执行该函数一次。
时间复杂度:O(n) n代表链表中元素的个数。
int clist_ins_next(CList* list, CListElement* element, const void* data);
返回值:如果成功插入链表返回0,出现错误返回-1
描述:将元素插入element
元素的后结点之后。当初如element
为空链表的时候,新元素可能加入链表的任何一个地方。为了避免出现错误,element
应该设置为NULL
。
时间复杂度:O(1) 插入的行为是固定时间的
int clist_rem_next(CList* list, CListElement* element, const void** data);
返回值:如果成功插入链表返回0,出现错误返回-1
描述:移除循环链表中元素element
后一个元素,参数const void** data
为移除的元素的数据地址给用户进行管理。
时间复杂度:O(1) 插入的行为是固定时间的。
宏定义
#define clist_size(list) ((list)->size)
#define clist_head(list) ((list)->head)
#define clist_data(element) ((element)->data)
#define clist_next(element) ((element)->next)
复杂度:O(1) 宏定义的时间都是固定的,为了提高代码的可读性
循环链表的头文件clist.h
#pragma once
#ifndef CLIST_H
#define CLIST_H
typedef struct CListElement_
{
void* data;
struct CListElement_* next;
}CListElement;
typedef struct CList_
{
int size;
int (*match)(const void* key1, const void* key2);
void (*destroy)(void* data);
CListElement* head;
}CList;
// 循环链表接口
void clist_init(CList* list, void (*destroy)(void* data));
void clist_destroy(CList* list);
int clist_ins_next(CList* list, CListElement* element, const void* data);
int clist_rem_next(CList* list, CListElement* element, const void** data);
#define clist_size(list) ((list)->size)
#define clist_head(list) ((list)->head)
#define clist_data(element) ((element)->data)
#define clist_next(element) (