循环链表简介
简单来说,单链表像一个小巷,无论怎么样最终都能从一端走到另一端,循环链表则像一个有传送门的小巷,因为循环链表当你以为你走到结尾的时候,其实你又回到了开头。循环链表和非循环链表其实创建的过程以及思路几乎完全一样,唯一不同的是,非循环链表的尾结点指向空(NULL),而循环链表的尾指针指向的是链表的开头。通过将单链表的尾结点指向头结点的链表称之为循环单链表(Circular linkedlist)。
如上图所示,循环链表将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环。循环链表与单链表的主要差异在于循环的判断上,原来是判断p->next是否为空,现在则是p->next不等于头结点,则循环未结束。
循环链表的实现
1、创建链表
如同单链表的创建,我们需要先创建一个头结点并且给其开辟内存空间,但与单链表不同的是,我们需要在开辟内存空间成功之后将头结点的next指向head自身。当需要进行插入时,我们首先创建一个新的节点,将原有链表尾结点的next指针修改指向到新的结点,新结点的next指针再重新指向头部结点,然后逐步进行这样的插入操作,最终完成整个单项循环链表的创建。代码示例如下:
int insert_list(list *head){
int data; //插入的数据类型
printf("请输入要插入的元素:");
scanf("%d",&data);
list *node=initlist();
node->data=data; //初始化一个新的结点,准备进行链接
if(head!=NULL){
list *p=head;
//找到最后一个数据
while(p->next!=head){
p=p->next;
}
p->next=node;
node->next=head;
return 1;
}else{
printf("头结点已无元素\n");
return 0;
}
}
2、删除操作
如图2所示,循环单链表的删除操作可以参考单链表的删除操作,其都是找到需要删除的结点,将其前一个结点的next指针直接指向删除结点的下一个结点即可,但需要注意的是尾节点和头结点的特判,尤其是尾结点,因为删除尾节点后,尾节点前一个结点就成了新的尾节点,这个新的尾节点需要指向的是头结点而不是空,其重点可以记录为当前的前一节点.next=自身结点.next这样的操作可以省去头尾结点的特判。
示例代码:
int delete_list(list *head) {
if(head == NULL) {
printf("链表为空!\n");
return 0;
}
list *temp = head;
list *ptr = head->next;
int del;
printf("请输入你要删除的元素:");
scanf("%d",&del);
while(ptr != head) {
if(ptr->data == del) {
if(ptr->next == head) {
temp->next = head;
free(ptr);
return 1;
}
temp->next = ptr->next;//核心删除操作代码
free(ptr);
//printf("元素删除成功!\n");
return 1;
}
temp = temp->next;
ptr = ptr->next;
}
printf("没有找到要删除的元素\n");
return 0;
}
循环链表删除功能的实现:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>
#define ERROR 0
#define OK 1
typedef int EleType;
typedef struct CLinkNode
{
EleType data;
struct CLinkNode *next;
}CLinkNode,*CLinkList;
/*
初始化循环链表
*/
int InitCLinkList(CLinkList *list)
{
if (list == NULL)
{
return ERROR;
}
int data = 0;
CLinkNode* target = NULL;
CLinkNode* head_node = NULL;
printf("输入结点数据中...\n");
while (1)
{
scanf("%d", &data);
if (data == 0)
{
//退出循环标志,用户输入0 表示结束输入数据
break;
}
if (*list == NULL)
{
CLinkNode* head= (CLinkNode*)malloc(sizeof(CLinkNode));
//分配结点空间失败
if (head == NULL)
{
exit(0);
}
*list = head;//链表指向头结点
CLinkNode* node = (CLinkNode*)malloc(sizeof(CLinkNode));
if (node == NULL)
{
exit(0);
}
node->data = data;
node->next = head;
head->next = node;
}
else
{
for (target = (*list)->next; target->next != *list; target = target->next);
head_node = target->next;
CLinkNode* node = (CLinkNode*)malloc(sizeof(CLinkNode));
if (node == NULL)
{
exit(0);
}
node->data = data;
node->next = head_node;
target->next = node;//将新结点插入尾部
}
}
return OK;
}
/*
往链表指定位置插入数据
list 循环链表
loc 第loc位置插入元素,loc 从1 开始计数
data 插入元素的数据域
*/
int InsertCLinkNode(CLinkList list,int loc, EleType data)
{
if (list == NULL || loc < 1)
return ERROR;
/*
循环目的:找到第loc-1位置结点
*/
int i = 1;
CLinkNode* node = list;//刚开始node指向头结点
while (node->next!=list && i < loc)
{
node = node->next;
i++;
}
if (i == loc)
{
CLinkNode* new_node = (CLinkNode*)malloc(sizeof(CLinkNode));
if (new_node == NULL)
{
exit(0);
}
new_node->data = data;
new_node->next = node->next;//新结点指针域 指向前驱结点的后继结点
node->next = new_node;//将新结点加入链表
}
else
{
return ERROR;
}
return OK;
}
/*
删除指定结点,通过指针返回删除结点的数据,并保存至data
*/
int DelCLinkNode(CLinkList list,int loc, EleType* data)
{
if (list == NULL || loc < 1)
return ERROR;
/*
循环目的:找到第loc-1位置结点
*/
int i = 1;// 按人类的读法 i表示第i个位置 和 loc 表达意思一致
CLinkNode* node = list;//刚开始node指向头结点
while (node->next != list && i < loc)
{
node = node->next;
i++;
}
//循环结束 node 指向 loc-1 位置 且 node 不能为尾结点,为什么不能为尾结点?因为不能删除 位置上没有元素的结点!
if (i == loc && node->next != list)
{
// 请在下面的Begin-End之间补充代码,完成对结点的删除。
/********** Begin *********/
CLinkNode* del_node = node->next; //第loc位置的结点
*data = del_node->data; //返回删除结点的数据域
node->next = del_node->next; //删除结点的前驱结点指向删除节点的后继结点
free(del_node);
/********** End **********/
}
return OK;
}
/*
展示循环链表元素
*/
int ShowCLinkList(CLinkList list)
{
if (list == NULL)
{
return ERROR;
}
CLinkNode* target = NULL;
for (target = list->next; target != list; target = target->next)
printf("%d \t",target->data);
printf("\n");
return OK;
}
int main(int argc, char *argv[])
{
int flag = 0;
CLinkList list = NULL;
list = NULL;
InitCLinkList(&list);
printf("--------循环链表初始元素------\n");
ShowCLinkList(list);
int loc = 2;
int data = 0;
DelCLinkNode(list, loc, &data);
printf("--------删除第二个结点后------\n");
ShowCLinkList(list);
return 0;
}