循环链表
定义
循环链表是一种特殊的链表,它的最后一个节点指向第一个节点,形成一个环状结构。具体来说,循环链表的最后一个节点的指针域指向头节点,而其他节点的指针域依次指向下一个节点。这样,我们可以通过任何一个节点来遍历整个链表,而且当遍历到链表的最后一个节点时,下一个节点又是头节点,从而形成循环。
特点
循环链表具有以下特点:
- 最后一个节点的指针指向第一个节点,形成一个闭环。
- 可以通过任意节点进行遍历,因为每个节点都有下一个节点的指针。
- 在遍历循环链表时,可以无限循环下去,不会出现尾部节点的空指针错误。
- 可以从任意节点开始遍历,因此可以选择一个合适的起始节点来提高操作效率。
- 不增加额外的存储开销,却能给很多操作带来方便。
循环链表的运算
循环链表的运算主要包括以下几种:
- 初始化
- 插入节点
- 删除节点
- 遍历
- 查找
- 其他操作:循环链表还可以进行修改节点值、反转链表等操作。
结构体
typedef int DataType;
/*简单链表的定义*/
typedef struct node
{
DataType data; /*数据域*/
struct node *next; /*指针域*/
}SingleLinkList, SingleLinkNode;
初始化
循环链表的初始化包括创建头结点,并令头结点的指针域指向自己。
/*1. 初始化*/
int init(SingleLinkList **Head)
{
if(1)
{
/*申请内存*/
(*Head) = (SingleLinkList*)malloc(sizeof(SingleLinkList));
/*判断内存申请是否成功*/
if(*Head == NULL)
{
printf("申请内存错误, 初始化失败![100001]\n");
return 100001;
}
(*Head)->next = *Head;
return 0;
}
else
{
printf("该链表已经初始化!请删除后再执行此操作![100002]\n");
return 100002;
}
}
插入
头插法
循环链表头插法是一种在循环链表中插入新节点的方法,它是指将新节点插入到链表的头部,即第一个节点之前,使得最后节点的next指向新的头节点,新节点的next指向旧的Head。
/*2. 插入元素,头插法*/
int insert_head(SingleLinkList **Head, DataType x)
{
SingleLinkNode *newNode;
if(0)
{
printf("链表未初始化![100003]\n");
return 100003;
}
newNode = (SingleLinkNode*)malloc(sizeof(SingleLinkNode));
if(!newNode)
{
printf("申请节点内存空间失败![100004]\n");
return 100004;
}
newNode->data = x;
newNode->next = (*Head)->next;
(*Head)->next = newNode;
return 0;
}
尾插法
循环链表的尾插法是一种在循环链表中插入新节点的方法,它是指将新节点插入到链表的尾部,即最后一个节点之后,使得新节点的next指向头节点,旧的尾节点next指向最后的尾节点。
/*2. 插入元素, 尾插法*/
int insert_tail(SingleLinkList **Head, DataType x)
{
SingleLinkNode *newNode;
SingleLinkNode *p;
if(0)
{
printf("链表未初始化![100003]\n");
return 100003;
}
newNode = (SingleLinkNode*)malloc(sizeof(SingleLinkNode));
if(!newNode)
{
printf("申请节点内存空间失败![100004]\n");
return 100004;
}
newNode->data = x;
newNode->next = *Head;
p = (*Head);
while(p->next != (*Head))
{
p = p->next;
}
p->next = newNode;
return 0;
}
在位置i处插入
单链表的第i个节点插入操作,需要找到第i-1个节点,将新节点插入到它的下一个节点位置,并将新节点的next指针指向第i+1个节点。
/*2. 插入元素,在位置i处插入元素x */
int insert(SingleLinkList **Head, int i, DataType x)
{
int j;
SingleLinkNode *p;
SingleLinkNode *newNode;
/*对i进行判断,0<i<=length+1*/
if(i<1 || i>length(*Head)+1)
{
printf("位置i不是链表有效位置![100005]\n");
return 100005;
}
p = (*Head);
j = 1;
while(j<i)
{
j++;
p = p->next;
}
newNode = (SingleLinkNode*)malloc(sizeof(SingleLinkNode));
/*此处省略检测newNode是否申请成功*/
newNode->data = x;
newNode->next = p->next;
p->next = newNode;
return 0;
}
删除
循环链表的删除操作需要找到需要删除的节点,修改其前驱节点的指针域,使其指向被删除节点的下一个节点;如果删除的是尾节点,则需要将尾指针指向倒数第二个节点。最后释放被删除节点的内存空间即可。
/*3. 删除元素, 删除值为x的元素*/
int delete(SingleLinkList **Head, DataType x)
{
int i;
int j;
SingleLinkNode *p;
SingleLinkNode *q; /*要删除的元素x*/
i = find(*Head,x);
if(!i)
{
printf("元素x【%d】不存在!100006\n", x);
return 100006;
}
p = (*Head);
j=1;
while(j<i)
{
j++;
p = p->next;
}
q = p->next;
p->next = q->next;
free(q); /*释放内存*/
return 0;
}
遍历
循环链表的遍历指的是按照一定的规则访问循环链表中的每个节点,从而获取节点的数据或执行某些操作。在循环链表中,每个节点都有一个指向下一个节点的指针,最后一个节点指向头节点,形成一个环。
查找值为x的元素,返回位置i
循环链表的长度可以通过遍历链表来获取。从头节点开始,依次遍历每个节点,直到回到头节点,同时记录节点的数量也为下标,当遍历值相等时返回下标,当遍历找不到相等值则返回0;
/*5. 查找值为x的元素,返回位置i */
int find(SingleLinkList *Head, DataType x)
{
int i;
SingleLinkNode *p;
i = 1;
p = Head->next;
while( p!= Head && p->data != x)
{
i++;
p = p->next;
}
if(p->next == Head)
{
return 0;
}
else
{
return i;
}
}
链表长度
循环链表的长度可以通过遍历链表来获取。从头节点开始,依次遍历每个节点,直到回到头节点,同时记录节点的数量,即为链表的长度。
int length(SingleLinkList *Head)
{
int len=0;
SingleLinkNode *p;
p = Head->next;
while(p!=Head)
{
len++;
p = p->next;
}
return len;
}
输出链表
循环链表的遍历输出操作可以通过从头节点开始,依次遍历每个节点的值,当遍历到尾节点时,再次回到头节点继续输出,直到遍历完整个链表。
void print(SingleLinkList *Head)
{
SingleLinkNode *p;
int i=0;
p = Head->next;
if(p==Head)
{
printf("链表为空!\n");
return;
}
while(p!=Head)
{
printf("Node[%d]. = %d\n", ++i, p->data);
p = p->next;
}
}
循环链表的实现
完整代码
项目结构
main.c
SingleLinkList.c
SingleLinkList.h
welcome.h
项目文件
main.c
#include <stdio.h>
#include <string.h>
#include "SingleLinkList.h"
#include "welcome.h"
int main(int argc, char* argv[])
{
SingleLinkList *Head;
DataType x;
int i,m,n,cmd;
for(i=0;i<strlen(welcome);i++)
{
printf("%c",welcome[i]);
for(m=0;m<1000;m++)
for(n=0;n<1000;n++)
{
;
}
}
printf("-----------简单链表演示程序----------\n");
do
{
printf("1. 初始化链表表\n");
printf("2. 插入元素(头插法)\n");
printf("3. 插入元素(尾插法)\n");
printf("4. 插入元素(在位置i插入)\n");
printf("5. 查找元素x\n");
printf("6. 求链表长度\n");
printf("7. 输出链表\n");
printf("8. 删除元素\n");
printf("10. 帮助\n");
printf("0. 退出\n");
printf("请输入您要进行的操作(1~6,0退出):");
scanf("%d", &cmd);
switch(cmd)
{
case 1:
if(!init(&Head))
{
printf("链表已初始化!\n");
}
break;
case 2:
printf("请输入插入元素x:x=");
scanf("%d",&x);
if(!insert_head(&Head,x))
{
printf("元素(%d)已插入\n", x);
}
break;
case 3:
printf("请输入插入元素x:x=");
scanf("%d",&x);
if(!insert_tail(&Head,x))
{
printf("元素(%d)已插入\n", x);
}
break;
case 4:
printf("请输入插入元素位置i和元素x(i,x):");
scanf("%d,%d", &i, &x);
if(!insert(&Head, i, x))
{
printf("已在位置(%d)插入元素(%d)!\n",i, x);
}
break;
case 5:
printf("请输入要查找的元素x:");
scanf("%d", &x);
if(i = find(Head,x))
{
printf("元素%d存在,在链表位置%d.\n", x, i);
}
else
{
printf("在链表中未找到元素x。\n");
}
break;
case 6:
printf("链表的长度为:%d\n", length(Head));
break;
case 7:
print(Head);
break;
case 8:
printf("请输入要删除的元素x:");
scanf("%d", &x);
if(!delete(&Head, x))
{
printf("元素x【%d】已删除!\n", x);
}
break;
case 10:
printf(" 本程序为链表的演示程序,靓仔设计开发,程序完成了。。。。功能!。。。\n");
break;
}
}while(cmd != 0);
return 0;
}
SingleLinkList.c
/*
SingleLinkList.c
*/
#include "SingleLinkList.h"
#include <stdlib.h>
#include <stdio.h>
/*1. 初始化*/
int init(SingleLinkList **Head)
{
if(1)
{
/*申请内存*/
(*Head) = (SingleLinkList*)malloc(sizeof(SingleLinkList));
/*判断内存申请是否成功*/
if(*Head == NULL)
{
printf("申请内存错误, 初始化失败![100001]\n");
return 100001;
}
(*Head)->next = *Head;
return 0;
}
else
{
printf("该链表已经初始化!请删除后再执行此操作![100002]\n");
return 100002;
}
}
/*2. 插入元素,头插法*/
int insert_head(SingleLinkList **Head, DataType x)
{
SingleLinkNode *newNode;
if(0)
{
printf("链表未初始化![100003]\n");
return 100003;
}
newNode = (SingleLinkNode*)malloc(sizeof(SingleLinkNode));
if(!newNode)
{
printf("申请节点内存空间失败![100004]\n");
return 100004;
}
newNode->data = x;
newNode->next = (*Head)->next;
(*Head)->next = newNode;
return 0;
}
/*2. 插入元素, 尾插法*/
int insert_tail(SingleLinkList **Head, DataType x)
{
SingleLinkNode *newNode;
SingleLinkNode *p;
if(0)
{
printf("链表未初始化![100003]\n");
return 100003;
}
newNode = (SingleLinkNode*)malloc(sizeof(SingleLinkNode));
if(!newNode)
{
printf("申请节点内存空间失败![100004]\n");
return 100004;
}
newNode->data = x;
newNode->next = *Head;
p = (*Head);
while(p->next != (*Head))
{
p = p->next;
}
p->next = newNode;
return 0;
}
/*2. 插入元素,在位置i处插入元素x */
int insert(SingleLinkList **Head, int i, DataType x)
{
int j;
SingleLinkNode *p;
SingleLinkNode *newNode;
/*对i进行判断,0<i<=length+1*/
if(i<1 || i>length(*Head)+1)
{
printf("位置i不是链表有效位置![100005]\n");
return 100005;
}
p = (*Head);
j = 1;
while(j<i)
{
j++;
p = p->next;
}
newNode = (SingleLinkNode*)malloc(sizeof(SingleLinkNode));
/*此处省略检测newNode是否申请成功*/
newNode->data = x;
newNode->next = p->next;
p->next = newNode;
return 0;
}
/*3. 删除元素, 删除值为x的元素*/
int delete(SingleLinkList **Head, DataType x)
{
int i;
int j;
SingleLinkNode *p;
SingleLinkNode *q; /*要删除的元素x*/
i = find(*Head,x);
if(!i)
{
printf("元素x【%d】不存在!100006\n", x);
return 100006;
}
p = (*Head);
j=1;
while(j<i)
{
j++;
p = p->next;
}
q = p->next;
p->next = q->next;
free(q); /*释放内存*/
return 0;
}
/*5. 查找值为x的元素,返回位置i */
int find(SingleLinkList *Head, DataType x)
{
int i;
SingleLinkNode *p;
i = 1;
p = Head->next;
while( p!= Head && p->data != x)
{
i++;
p = p->next;
}
if(p->next == Head)
{
return 0;
}
else
{
return i;
}
}
/*4. 链表长度*/
int length(SingleLinkList *Head)
{
int len=0;
SingleLinkNode *p;
p = Head->next;
while(p!=Head)
{
len++;
p = p->next;
}
return len;
}
/*6.输出链表*/
void print(SingleLinkList *Head)
{
SingleLinkNode *p;
int i=0;
p = Head->next;
if(p==Head)
{
printf("链表为空!\n");
return;
}
while(p!=Head)
{
printf("Node[%d]. = %d\n", ++i, p->data);
p = p->next;
}
}
SingleLinkList.h
/*
SingleLinkList.h
*/
typedef int DataType;
/*简单链表的定义*/
typedef struct node
{
DataType data; /*数据域*/
struct node *next; /*指针域*/
}SingleLinkList, SingleLinkNode;
/*1. 初始化*/
int init(SingleLinkList **Head);
/*2. 插入元素,头插法*/
int insert_head(SingleLinkList **Head, DataType x);
/*2. 插入元素, 尾插法*/
int insert_tail(SingleLinkList **Head, DataType x);
/*2. 插入元素,在位置i处插入元素x */
int insert(SingleLinkList **Head, int i, DataType x);
/*3. 删除元素, 删除值为x的元素*/
int delete(SingleLinkList **Head, DataType x);
/*5. 查找值为x的元素,返回位置i */
int find(SingleLinkList *Head, DataType x);
/*6. 求链表的长度 */
int length(SingleLinkList *Head);
/*7.输出链表*/
void print(SingleLinkList *Head);
welcome.h
char welcome[] = "\n\
/\ \n\
( *)======/\==== \n\
)( / \ \n\
__________/ ) / \ \n\
\___ / / \"\" \ \n\
\____ _/ / (**) \ \n\
/ \__/ (----------) \n\
/____|__//_ ( 送给您- ) \n\
| ( 亲爱的 ) \n\
| ( )\n\
| (____)\n\
_|__\n\
\\ ☆新年 . 快乐☆\n\n";
运行结果
小结
循环链表是一种链表结构,其中最后一个节点的指针指向头节点,形成一个环状数据结构。
循环链表在某些应用场景中比普通链表更加方便和高效。例如,在使用队列时,我们需要不断地在队尾插入元素并在队头删除元素。使用循环链表可以避免在进行插入和删除操作时手动分配内存和释放内存的麻烦和开销。同时,相对于普通链表循环链表也可以在有限的内存空间内存储更多的数据元素。
总之,循环链表是一种非常实用的数据结构,它可以在某些场景下比普通链表更加高效和方便地实现数据的存储和操作。
参考文献
【数据结构与算法】- 循环链表 - 详细实现步骤及代码(C/C++)_c++循环链表_wkd_007的博客-CSDN博客