有了单向链表的实现,但是单向链表的遍历需要从头节点开始,直到尾节点结束,这就会有许多限制,比方说在不知道头节点的情况下,就无法通过单向链表来遍历,这时候就需要单向循环链表。单向循环链表可以从任意节点位置开始,一直循环遍历到该节点本身结束,只需要知道任意一个节点。
而且单向链表在进行尾插法的时候,需要特殊处理,因为尾节点没有下一个节点,而单向循环链表中,尾节点的下一个节点就是头节点,操作上更加统一和简便。
1.先将数据域定义出来
typedef int Elenment;
然后定义单向循环链表的节点结构
typedef struct loopNode{
Elenment val; //val 指这个节点所包含的数据
struct loopNode *next; //next指这个节点指向下一个节点的指针
} LoopNode;
有了节点结构,还需要定义他的头结构
typedef struct{
LoopNode header; //header表示LoopNode类型的成员变量
LoopNode *rear; //*rear是指向LoopNode类型的尾指针
int num;
}LinkLoopList;
现在头节点和节点结构定义完了,就可以定义接口
2.静态初始化接口
void initLinkLoopList(LinkLoopList *link_loop){
link_loop->num = 0; //将链表的元素数量初始化为0,当前链表尾空
link_loop->rear = &link_loop->header; //将链表的尾指针指向链表的头节点
link_loop->header.next = &link_loop->header; //将头节点的next指向指针自身,形成一个闭环,形成一个循环链表的初始化状态,是循环链表的基本特征
}
3.然后就可以开始进行插入操作
3.1头插法
传入链表的指针,和待插入的数据
int insertLinkLoopHeader(LinkLoopList *link_loop, Element value){
//1.先有新节点
LoopNode *node = malloc(sizeof(LoopNode)); //申请一片空间,用作待插入节点的空间
if(node ==NULL){ //申请失败
return -1;
}
//2.处理关系,先处理新的关系,再处理老的关系
node->val = value; //将待插入的值赋值给这个节点的val成员
node->next = link_loop->header.next; //这个节点指向下一个节点的指针指向头节点的下一个节点
link_loop->header.next = node; //头节点指向下一个节点的指针指向新节点
//头->B->C 插入A ==> 头 A->B->C ==> 头->A->B->C
//3.判断尾指针是否需要初始化
if(link_loop->rear == &link_loop->header){ //判断尾指针是否指向链表的头节点,如果是,则说明链表之前可能为空或只有一个节点,此时要更新尾指针
link_loop->rear = node; //尾指针要指向最后一个节点
}
++link_loop->num; //插入成功后,更新节点的数量
return 0;
}
3.2尾插法
传入链表的指针,和待插入的数据
int insertLinkLoopRear(LinkLoopList *link_loop, Element value){
//1.先有新节点
LoopNode *node = malloc(sizeof(LoopNode)); //申请一片空间,用作待插入节点的空间
if (node == NULL) { //申请失败
return -1;
}
//2.处理关系,先处理新关系,再处理老关系
node->val = value; //将待插入的值赋值给这个节点的val成员
//将新节点的next指针指向link_loop->rear->next,也就是头节点,以完成循环
node->next = link_loop->rear->next; //让新节点的next指针指向当前链表尾部节点的下一个节点
link_loop->rear->next = node; //将当前链表尾部节点的next指针指向新节点,完成新节点与链表的链接,完成循环
// 3. rear指针的更新
if(link_loop->rear == &link_loop->header){ //如果链表里没有元素
link_loop->rear == node; //将链表的尾指针指向新节点
link_loop->header.next = node; //将链表的头节点的next指向新节点
}else{
link_loop->rear = node; //如果链表里有元素,直接更新尾指针,头节点的next节点与尾插法关系不大
}
++link_loop->num; //插入完成,更新链表节点数量
return 0;
}
将数据都插入好后,封装一个用来显示的函数
通过引入一个辅助指针,来进行遍历循环链表
void showLinkLoopList(const LinkLoopList *link_loop){
LoopNode *node = link_loop->header.next; //引入辅助指针指向头节点的下一个节点,是第一个有效节点
while(node != &link_loop->header){ //由于是循环链表,最后一个节点的下一个节点就是头节点,所以条件只要不等于头节点就不会重复
printf("\t%d",node->val); //输出当前所在的节点
node = node->next; //依次往后遍历循环
}
printf("\n");
}
最后要删除这片空间
同样也是引用辅助指针,和一个临时指针,防止释放后找不到下一个节点的内容导致释放失败
void releaseLoopList(LinkLoopList *link_loop){
LoopNode *node = link_loop->header.next; //指向头节点的下一个节点,也是第一个有效节点
while(node != &link_loop->header){ 由于是循环链表,最后一个节点的下一个节点就是头节点,所以条件只要不等于头节点就不会重复
LoopNode *tmp = node; //辅助指针指向待删除的空间
node = node->next; //继续向下遍历,这样就不会找不到下面的空间
free(tmp); //释放掉临时指针指向的待删除空间
--link_loop->num; //释放掉一个,个数减1
}
}