循环链表是链表的另一种形式,它提供了更灵活的遍历链表元素的能力。
循环链表分类:单向循环链表和双向循环链表。
区分一个链表是不是循环链表只要看它有没有尾元素即可。在一个循环链表中,最后一个元素的next指针又指回头元素,而不是被设置为NULL。在双向循环链表中,头元素的prev指针则指向最后一个元素,这就使得循环链表中的每个元素即可以看做是头元素也可以看作是尾元素。
和非循环链表不同,在遍历循环链表元素时,不需要担心到达链表尾部而无法继续遍历下去了,相反,遍历过程会继续从链表的头元素开始,如果是双向循环链表,还可以继续从尾元素反射遍历。这种形式的遍历就形成了一种循环(见图1),这也是循环链表得名的原因。
我们将围绕单向循环链表展开学习。我们需要考虑的仅仅是维护尾元素与头元素的关系,使尾元素的next指针指向头元素。在实际应用中到底选择单向循环链表还是双向循环链表,理由如同选择非循环的单向链表或双向链表。
循环链表接口的定义
clist_init |
void clist_init(CList *list, void (*destroy)(void *data)); |
返回值:无 |
描述: 用来初始化由参数list所指定的循环链表。该函数必须在循环链表做任何其他操作之前调用。当clist_destroy被调用时,这里传入的destroy参数提供了一种释放动态分配空间的方法。它的工作方式同前文叙述的list_destroy相似。对于循环链表,如果其中包含不需要手动释放空间的数据,则destroy参数应该被设置为NULL。 |
复杂度 O(1) |
clist_destroy |
void clist_destroy(CList *list); |
返回值:无 |
描述: 销毁由list所指定的循环链表。调用clist_destroy之后,其他任何任何操作都不允许再执行,除非用户再次调用clist_init。函数clist_destroy将循环链表中的所有元素都移除。如果传给clist_destroy的参数destroy不为NULL的话,则调用destroy所指定的函数,对链表中每个移除的元素数据施行资源回收操作。 |
复杂度 O(n) 这里的n代表循环链表中的元素个数。 |
clist_ins_next |
int clist_ins_next(CList *list,CListElmt *element,const void *data); |
返回值:如果插入操作成功则返回1,否则返回0。 |
描述: 将元素插入由list指定的循环链表中element元素之后。当插入空链表中时,element可能指向任何位置,为了避免混淆,element此时应该设置为NULL。新的元素包含一个指向data的指针,因此只要该元素仍在链表中时,data所引用的内存空间就应该保持合法。由调用者管理data所引用的存储空间。 |
复杂度 O(1) |
clist_rem_next |
int clist_rem_next(CList *list,CListElmt *element,const void **data); |
返回值:如果移除操作成功则返回0,否则返回-1。 |
描述: 移除由参数list所指定的链表中element后面的元素。返回时,参数data将指向已移除元素中存储的数据。由调用者管理与data相关联的存储空间。所插入链表是空链表时,必须将所插入元素的next指针设置为指向它自己。这就允许循环遍历只有一个元素的链表。也确保了之后的元素的插入操作能够按恰当的方式进行。 |
复杂度 O(1) |
clist_size |
int clist_size(Const CList *list); |
返回值:链表中元素的个数。 |
描述: 这是一个宏,用来计算由参数list所指定的链表中的元素的个数。 |
复杂度 O(1) |
clist_head |
CListElmt *clist_head(Const CList *list); |
返回值:返回链表头的元素。 |
描述: 这是一个宏,用来返回由参数list所指定的循环链表中的头元素。 |
复杂度 O(1) |
clist_data |
void *clist_data(Const CListElmt *element); |
返回值:返回元素中存储的数据域。 |
描述: 这是一个宏,用来返回循环链表中由参数element所指定的元素中存储的数据域。 |
复杂度 O(1) |
clist_next |
CListElmt *clist_next(Const CListElmt *element); |
返回值:返回由element所指定元素的后继元素。 |
描述: 这是一个宏,用来返回循环链表中由参数element所指定的元素的后继元素。 |
复杂度 O(1) |