算法和数据结构总结---循环链表

本文介绍了循环链表的概念和特点,重点解析了循环链表的初始化、销毁、元素插入和删除的函数实现,包括clist_init、clist_destroy、clist_ins_next和clist_rem_next等操作,强调了它们的时间复杂度,并提供了相关宏定义和源代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

循环链表不同于单向链表和双向链表,它比起前两者具有更高的灵活性,和单向链表和双向链表不同,循环链表不用担心遍历到链表的尾巴而无法进行遍历下去。循环链表的最后一个元素不是指向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) ((element)->next)
#endif

循环链表函数的实现原理

void clist_init(CList* list, void (destroy)(void data));

链表初始化的操作很简单,只要创造一个空链表就可以,和普通的链表不同就是没有tail成员。

void clist_init(CList * list, void (*destroy)(void* data))
	{
		list->size = 0;
		list->destroy = destroy;
		list->head = NULL;
		
		return;
	}
void clist_destroy(CList* list);

void clist_destroy(CList* list);函数用来销毁一个循环链表,换句话说就是销毁循环链表的全部元素,那执行list_init函数时赋予形参的值不是NULL,每移除一个元素就会调用一次destroy函数。

void clist_destroy(CList* list) 
	{
		void* data;
		while (clist_size(list) > 0) 
		{
			if (clist_remove(list,list->head,(void**)&data) == 0 && list->destroy != NULL)
			{
				list->destroy(data);
			}
		}
		memset(list, 0, sizeof(CList));
		return;
	}

int clist_ins_next(CList* list, CListElement* element, const void* data);

基本循环链表的后插和基本的链表的后插是类似,但是不同在element的值为NULL是要把插入元素的next指向自身,这个就保证允许只存在一个元素的循环链表的存在。

int  clist_ins_next(CList* list, CListElement* element, const void* data) 
	{
		CListElement* new_element;
		if ((new_element = (CListElement*)malloc(sizeof(CListElement))) == NULL)
		{
			return -1;
		}
		new_element->data = (void*)data;
		if (clist_size(list) == 0)
		{
			list->head = new_element;
			new_element->next = new_element;
		}
		else
		{
			new_element->next = element->next;
			element->next = new_element;
		}
		list->size++;
		return 0;
	}

int clist_rem_next(CList* list, CListElement* element, const void** data);

具体的做法和普通链表的移除元素类似。

int  clist_rem_next(CList* list, CListElement* element, const void** data)
	{
		CListElement* old_element;
		if (clist_size(list) == 0)
		{
			return -1;
		}
		*data = element->next->data;
		if (element->next == element)
		{
			old_element = element->next;
			list->size = 0;
		}
		else
		{
			old_element = element->next;
			element->next = element->next->next;
			if (old_element == clist_head(list))
			{
				list->head = old_element->next;
			}
		}
		free(old_element);
		list->size--;
		return 0;
	}

完整代码:

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) ((element)->next)
#endif

clist.c文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "clist.h"

	// 循环链表接口
	//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_ins_prev(CList * list, CListElement * element, const void* data);
	//int  clist_remove(CList * list, CListElement * element, const void* data);
	void clist_init(CList * list, void (*destroy)(void* data))
	{
		list->size = 0;
		list->destroy = destroy;
		list->head = NULL;
		
		return;
	}

	void clist_destroy(CList* list) 
	{
		void* data;
		while (clist_size(list) > 0) 
		{
			if (clist_remove(list,list->head,(void**)&data) == 0 && list->destroy != NULL)
			{
				list->destroy(data);
			}
		}
		memset(list, 0, sizeof(CList));
		return;
	}

	int  clist_ins_next(CList* list, CListElement* element, const void* data) 
	{
		CListElement* new_element;
		if ((new_element = (CListElement*)malloc(sizeof(CListElement))) == NULL)
		{
			return -1;
		}
		new_element->data = (void*)data;
		if (clist_size(list) == 0)
		{
			list->head = new_element;
			new_element->next = new_element;
		}
		else
		{
			new_element->next = element->next;
			element->next = new_element;
		}
		list->size++;
		return 0;
	}

	int  clist_rem_next(CList* list, CListElement* element, const void** data)
	{
		CListElement* old_element;
		if (clist_size(list) == 0)
		{
			return -1;
		}
		*data = element->next->data;
		if (element->next == element)
		{
			old_element = element->next;
			list->size = 0;
		}
		else
		{
			old_element = element->next;
			element->next = element->next->next;
			if (old_element == clist_head(list))
			{
				list->head = old_element->next;
			}
		}
		free(old_element);
		list->size--;
		return 0;
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值