详解循环链表

一、基本概念

循环链表是一种特殊的链表,它的最后一个节点的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语言版)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值