一、基本概念
循环链表是一种特殊的链表,它的最后一个节点的next指针指向链表的头节点,形成一个环形结构。
二、定义
循环链表由节点组成,每个节点包含一个数据元素和一个指向下一个节点的指针。最后一个节点的指针指向链表的头节点,形成一个环形结构。
优点:
链表是一种动态排列,可以通过分配和刷新内存在运行时增长和收缩。对于链表,无论是单链表、双链表还是循环链表,都不需要知道元素和分配内存,因为它可以在必要时分配。
可以有效地处理循环链表的插入和删除,而无需重新构造链表。插入或删除元素后无需移动元素,只需更新下一个指针中存在的地址。
缺点:
循环链表的操作相对复杂,需要特别处理循环结束条件和遍历。
需要注意循环链表的特殊性,避免出现无限循环的情况。
三、代码实现
1.接口定义
/*
LinkList.h
*/
typedef int DataType;
/*链表的定义*/
typedef struct node
{
DataType data; /*数据域*/
struct node *next; /*指针域*/
}LinkList, LinkNode;
/*1. 初始化*/
int initList(LinkList **Head);
/*2. 插入元素,头插法*/
int insert_head(LinkList **Head, DataType x);
/*2. 插入元素, 尾插法*/
int insert_tail(LinkList **Head, DataType x);
/*2. 插入元素,在位置i处插入元素x */
int insertData(LinkList **Head, int i, DataType x);
/*3. 删除元素, 删除值为x的元素*/
int deleteData(LinkList **Head, DataType x);
/*5. 查找值为x的元素,返回位置i */
int findData(LinkList *Head, DataType x);
/*6. 求链表的长度 */
int length(LinkList *Head);
/*7.输出链表*/
void printList(LinkList *Head);
2.接口实现
2.1 循环链表初始化
/*1. 链表初始化*/
int initList(LinkList **Head)
{
if(1)
{
/*申请内存*/
(*Head) = (LinkList*)malloc(sizeof(LinkList));
/*判断内存申请是否成功*/
if(*Head == NULL)
{
printf("申请内存错误,初始化失败![100001]\n");
return 100001;
}
(*Head)->next = *Head;
return 0;
}
else
{
printf("该链表已经初始化!请删除后再执行此操作![100002]\n");
return 100002;
}
}
2.2 链表插入 (头插法、尾插法、按位插入)
/*2. 头插法*/
int insert_head(LinkList **Head, DataType x)
{
LinkNode *newNode;
if(0)
{
printf("链表未初始化![100003]\n");
return 100003;
}
newNode = (LinkNode*)malloc(sizeof(LinkNode));
if(!newNode)
{
printf("申请节点内存空间失败![100004]\n");
return 100004;
}
newNode->data = x;
newNode->next = (*Head)->next;
(*Head)->next = newNode;
return 0;
}
/*2. 尾插法*/
int insert_tail(LinkList **Head, DataType x)
{
LinkNode *newNode;
LinkNode *p;
if(0)
{
printf("链表未初始化![100003]\n");
return 100003;
}
newNode = (LinkNode*)malloc(sizeof(LinkNode));
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 insertData(LinkList **Head, int i, DataType x)
{
int j;
LinkNode *p;
LinkNode *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 = (LinkNode*)malloc(sizeof(LinkNode));
/*此处省略检测newNode是否申请成功*/
newNode->data = x;
newNode->next = p->next;
p->next = newNode;
return 0;
}
2.3 按值删除
/*3. 按值删除*/
// 删除值为x的元素
int deleteData(LinkList **Head, DataType x)
{
int i;
int j;
LinkNode *p;
LinkNode *q; /*要删除的元素x*/
i = findData(*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;
}
2.4 按值查找元素
/*5. 查找值为x的元素,返回位置i */
int findData(LinkList *Head, DataType x)
{
int i;
LinkNode *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;
}
}
2.5 链表长度
/*4. 链表长度*/
int length(LinkList *Head)
{
int len=0;
LinkNode *p;
p = Head->next;
while(p!=Head)
{
len++;
p = p->next;
}
return len;
}
2.6 输出链表
/*6.输出链表*/
void printList(LinkList *Head)
{
LinkNode *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;
}
}
2.7 main方法
#include <stdio.h>
#include <string.h>
#include "LinkList.h"
// #include "welcome.h"
char welcome[] = "\n// _ooOoo_ //\n// o8888888o //\n// 88”.“88 //\n// (| ^_^ |) //\n// O\ = /O //\n// ____/`---'\____ //\n// .' \\| |// `. //\n// / \\||| : |||// \ //\n// / _||||| -:- |||||- \ //\n// | | \\\ - /// | | //\n// | \_| ''\---/'' | | //\n// \ .-\__ `-` ___/-. / //\n// ___`. .' /--.--\ `. . ___ //\n// .‘’ '< `.___\_<|>_/___.' >' ‘’. //\n// | | : `- \`.;`\ _ /`;.`/ - ` : | | //\n// \ \ `-. \_ __\ /__ _/ .-` / / //\n// ========`-.____`-.___\_____/___.-`____.-'======== //\n// `=---=' //\n// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //\n// 佛祖保佑 永不宕机 永无BUG //\n\n";
int main(int argc, char* argv[])
{
LinkList *Head;
DataType x;
int i, m,n,cmd;
for(int i=0;i<strlen(welcome);i++)
{
printf("%c",welcome[i]);
for(int m=0;m<10000;m++)
for(int n=0;n<300;n++)
{
}
}
printf("-----------循环链表演示程序----------\n");
do
{
printf("1. 初始化循环链表\n");
printf("2. 插入元素(头插法)\n");
printf("3. 插入元素(尾插法)\n");
printf("4. 在位置i插入元素\n");
printf("5. 查找元素\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(!initList(&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("请输入插入元素位置:");
scanf("%d",&i);
printf("请输入要插入的元素:");
scanf("%d",&x);
if(!insertData(&Head, i, x))
{
printf("已在位置(%d)插入元素(%d)!\n",i, x);
}
break;
case 5:
printf("请输入要查找的元素x:");
scanf("%d", &x);
if(i = findData(Head,x))
{
printf("元素%d存在,在链表位置%d.\n", x, i);
}
else
{
printf("在链表中未找到元素x。\n");
}
break;
case 6:
printf("链表的长度为:%d\n", length(Head));
break;
case 7:
printList(Head);
break;
case 8:
printf("请输入要删除的元素x:");
scanf("%d", &x);
if(!deleteData(&Head, x))
{
printf("元素x【%d】已删除!\n", x);
}
break;
case 10:
printf(" 本程序为链表的演示程序,由杜露设计开发,本程序演示了循环链表功能!\n");
break;
}
}while(cmd != 0);
return 0;
}
3.程序演示
四、扩展练习
一元多项式相加
#include <stdio.h>
#include <stdlib.h>
char welcome[] = "\n// _ooOoo_ //\n// o8888888o //\n// 88”.“88 //\n// (| ^_^ |) //\n// O\ = /O //\n// ____/`---'\____ //\n// .' \\| |// `. //\n// / \\||| : |||// \ //\n// / _||||| -:- |||||- \ //\n// | | \\\ - /// | | //\n// | \_| ''\---/'' | | //\n// \ .-\__ `-` ___/-. / //\n// ___`. .' /--.--\ `. . ___ //\n// .‘’ '< `.___\_<|>_/___.' >' ‘’. //\n// | | : `- \`.;`\ _ /`;.`/ - ` : | | //\n// \ \ `-. \_ __\ /__ _/ .-` / / //\n// ========`-.____`-.___\_____/___.-`____.-'======== //\n// `=---=' //\n// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //\n// 佛祖保佑 永不宕机 永无BUG //\n\n";
// 定义循环单链表节点结构
typedef struct Node {
int c; // 系数
int e; // 指数
struct Node *next; // 指向下一个节点的指针
} Node;
// 定义循环单链表结构
typedef struct LinkedList {
Node *head; // 头节点
} LinkedList;
// 初始化循环单链表
void initLinkedList(LinkedList *list) {
//初始化先指向空,存储节点后再指向head构成循环链表结构
list->head = NULL;
}
// 在循环单链表尾部插入一个节点
int insertNode(LinkedList *list, int c, int e) {
//申请内存
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->c = c;
newNode->e = e;
newNode->next = NULL;
if (list->head == NULL) {
list->head = newNode;
// 将尾节点的next指针指向头节点,构成循环链表
newNode->next = newNode;
} else {
Node *temp = list->head;
while (temp->next != list->head) {
temp = temp->next;
}
temp->next = newNode;
newNode->next = list->head;
}
return 0;
}
// 打印循环单链表
int printLinkedList(LinkedList *list) {
if (list->head == NULL) {
printf("这链表是空的![10001]\n");
return 10001;
}
Node *temp = list->head;
do {
//打印系数不为零的项
if (temp->c != 0)
{
printf("%dx^%d, ", temp->c, temp->e);
}
temp = temp->next;
} while (temp != list->head);
printf("\n");
return 0;
}
// 一元多项式相加
LinkedList add(LinkedList *poly1, LinkedList *poly2) {
//创建新的链表,用来存储相加后的结果
LinkedList result;
//初始化链表
initLinkedList(&result);
Node *temp1 = poly1->head;
Node *temp2 = poly2->head;
do {
//判断系数是否相等,如果相等就将相同系数项相加
if (temp1->e == temp2->e) {
int sum = temp1->c + temp2->c;
if (sum != 0) {
insertNode(&result, sum, temp1->e);
}
temp1 = temp1->next;
temp2 = temp2->next;
} else if (temp1->e > temp2->e) {//系数不同,直接插入
insertNode(&result, temp1->c, temp1->e);
temp1 = temp1->next;
} else{
insertNode(&result, temp2->c, temp2->e);
temp2 = temp2->next;
}
} while (temp1 != poly1->head && temp2 != poly2->head);
//将两个多项式节点插入到新的链表中
while (temp1 != poly1->head) {
insertNode(&result, temp1->c, temp1->e);
temp1 = temp1->next;
}
while (temp2 != poly2->head) {
insertNode(&result, temp2->c, temp2->e);
temp2 = temp2->next;
}
//返回结果
return result;
}
int main() {
LinkedList poly1, poly2, result;
initLinkedList(&poly1);
initLinkedList(&poly2);
int c, e,i,num1, num2;
for(int i=0;i<strlen(welcome);i++)
{
printf("%c",welcome[i]);
for(int m=0;m<10000;m++)
for(int n=0;n<300;n++)
{
}
}
while (i != 1)
{
printf("--------------------------------\n");
printf("一元多项式\n程序演示[0]:\n");
printf("退出:[1]\n");
scanf("%d",&i);
printf("请输入第一个多项式的项数: ");
scanf("%d", &num1);
printf("请输入多项式中每个项的系数和指数(空格隔开):\n");
for (int i = 0; i < num1; i++) {
scanf("%d %d", &c, &e);
insertNode(&poly1, c, e);
}
printf("请输入第二个多项式的项数: ");
scanf("%d", &num2);
printf("请输入多项式中每个项的系数和指数(空格隔开):\n");
for (int i = 0; i < num2; i++) {
scanf("%d %d", &c, &e);
insertNode(&poly2, c, e);
}
printf("第一个多项式: ");
printLinkedList(&poly1);
printf("第二个多项式: ");
printLinkedList(&poly2);
result = add(&poly1, &poly2);
printf("相加结果: ");
printLinkedList(&result);
printf("---------------END-----------------\n");
}
return 0;
}
结果演示
五、小结:
循环链表是一种特殊的链表,它的最后一个节点的next指针指向链表的头节点,形成一个环形结构。循环链表在特定问题的实现中具有一定的优势,例如约瑟夫环问题。然而,循环链表的操作相对复杂,需要特别处理循环结束条件和遍历。在使用循环链表时,需要注意循环链表的特殊性,避免出现无限循环的情况。
参考文献
优快云、数据结构(C语言版)