一、基本概念
循环链表的定义:将单链表中的最后一个元素next指针指向第一个元素
和我们上面所写的链表除了已经具备的基本功能以外,还要新增一个功能:游标的定义
在循环链表中可以定义一个“当前”指针,这个指针通常被称为游标,可以通过这个游标来遍历链表中的所有元素。
新增功能:
(1)将游标重置指向链表中的第一个数据元素
(2)获取当前游标指向的数据元素
(3)将游标移动指向到链表中的下一个数据元素
(4)直接指定删除链表中的某个元素
其中(1)(2)(3)都是和我们的游标slider相关的,而(4)是我们新添加的一个方法,根据输入的值去删除结点。
问题:
1.如何证明是循环链表?
使用CircleList_Get方法,放进一个循环内,循环的条件判断句是,链表总长度*2,这样就自然会遍历的时候,从头到尾1234再来一次1234,(假设链表的结点就是1234),那么我们就会输出12341234,就完成了一次循环链表的遍历
二、设计与实现
还是先书写header文件,因为和之前的方法相比,只是新增加了四个方法,所以前面的方法和参数可以照抄,然后再新添加四个方法
#ifndef CPRIMERPLUS_HEADER_H
#define CPRIMERPLUS_HEADER_H
typedef void CircleList;
typedef struct _tag_CircleListNode
{
struct _tag_CircleListNode * next;
}CircleListNode;
CircleList* CircleList_Create();
void CircleList_Destroy(CircleList* list);
void CircleList_Clear(CircleList* list);
int CircleList_Length(CircleList* list);
int CircleList_Insert(CircleList* list, CircleListNode* node, int pos);
CircleListNode* CircleList_Get(CircleList* list, int pos);
CircleListNode* CircleList_Delete(CircleList* list, int pos);
//新添加的四个方法
//(1)将游标重置指向链表中的第一个数据元素
CircleList *circleList_Reset(CircleList *list);
//(2)获取当前游标指向的数据元素
CircleList *circleList_current(CircleList *list);
//(3)将游标移动指向到链表中的下一个数据元素
CircleList *circleList_Next(CircleList *list);
//(4)直接指定删除链表中的某个元素
CircleList *circleList_DeleteMode(CircleList *list);
#endif //CPRIMERPLUS_HEADER_H
紧接着,还是写我们的main方法,将header.c头文件拟定好的方法串起来,实现一整套的功能,另外,嵌入元素还是使用的头插法,会在后续双向链表中使用尾插法。
#include <stdio.h>
#include <stdlib.h>
#include "header.h"
struct Value
{
CircleListNode circlenode;
int v;
};
int main()
{
CircleList* list = CircleList_Create();
struct Value v1;
struct Value v2;
struct Value v3;
struct Value v4;
struct Value v5;
struct Value v6;
struct Value v7;
struct Value v8;
int i = 0;
v1.v = 1;
v2.v = 2;
v3.v = 3;
v4.v = 4;
v5.v = 5;
v6.v = 6;
v7.v = 7;
v8.v = 8;
CircleList_Insert(list, (CircleListNode*)&v1, 0);
CircleList_Insert(list, (CircleListNode*)&v2, 0);
CircleList_Insert(list, (CircleListNode*)&v3, 0);
CircleList_Insert(list, (CircleListNode*)&v4, 0);
CircleList_Insert(list, (CircleListNode*)&v5, 0);
CircleList_Insert(list, (CircleListNode*)&v6, 0);
CircleList_Insert(list, (CircleListNode*)&v7, 0);
CircleList_Insert(list, (CircleListNode*)&v8, 0);
for(i=0; i<2*CircleList_Length(list); i++) //怎么样证明是循环链表
{
struct Value* pv = (struct Value*)CircleList_Get(list, i);
printf("%d\n", pv->v);
}
while( CircleList_Length(list) > 0 )
{
CircleList_Delete(list, 0);
}
printf("\n");
for(i=0; i<CircleList_Length(list); i++)
{
//获取游标所指元素,然后游标下移
struct Value* pv = (struct Value*)circleList_Next(list);
printf("%d\n", pv->v);
}
printf("\n");
//重置游标
circleList_Reset(list);
while( CircleList_Length(list) > 0 )
{
struct Value* pv = NULL;
for(i=1; i<3; i++)
{
circleList_Next(list);
}
pv = (struct Value*)circleList_current(list);
printf("%d\n", pv->v);
circleList_DeleteMode(list, (CircleListNode*)pv);
}
CircleList_Destroy(list);
return 0;
}
其实和之前的区别就在于在循环链表的main方法中添加了关于游标的实现。
下面开始写具体方法的实现,下面只介绍关于游标的三个方法,和新添加的根据Value删除结点的Delete_Node方法,其余方法可以在之前的博客中找到。
//把当前位置返回,并且游标下移
CircleListNode* circleList_Next(CircleList* list)
{
TCircleList* sList = (TCircleList*)list;
CircleListNode* ret = NULL;
if( (sList != NULL) && (sList->slider != NULL) )
{
ret = sList->slider;
sList->slider = ret->next;
}
return ret;
}
CircleListNode* circleList_DeleteMode(CircleList* list, CircleListNode* node) // O(n)
{
TCircleList* sList = (TCircleList*)list;
CircleListNode* ret = NULL;
int i = 0;
if( sList != NULL )
{
CircleListNode* current = (CircleListNode*)sList;
//查找node在循环链表中的位置i
for(i=0; i<sList->length; i++)
{
if( current->next == node )
{
ret = current->next;
break;
}
current = current->next;
}
//如果ret找到,根据i去删除
if( ret != NULL )
{
CircleList_Delete(sList, i);
}
}
return ret;
}
CircleListNode* circleList_Reset(CircleList* list) // O(1)
{
TCircleList* sList = (TCircleList*)list;
CircleListNode* ret = NULL;
if( sList != NULL )
{
sList->slider = sList->header.next;
ret = sList->slider;
}
return ret;
}
CircleListNode* circleList_current(CircleList* list) // O(1)
{
TCircleList* sList = (TCircleList*)list;
CircleListNode* ret = NULL;
if( sList != NULL )
{
ret = sList->slider;
}
return ret;
}
三、优点和缺点
优点:只是比单链表的功能强了,多了几个功能
缺点:代码复杂度高了