• 【数据结构】循环链表的增删查改



    前言

    本文我们所要实现的是循环链表,即带头双向循环链表


    一、什么是带头双向循环链表

    带头双向循环链表与无头单向非循环链表不同的是,它结构复杂,一般用于单独存储数据,实际中使用的链表数据结构,均是带头双向循环链表。而无头单向非循环链表结构简单,一般不会用来单独存储数据,实际更多用于其他数据结构的子结构

    在这里插入图片描述


    二、带头双向循环链表的实现

    在实现循环链表增删查改之前,首先应该创建一个循环链表

    typedef int CLTDataType;
    
    typedef struct CListNode
    {
    	struct CListNode* next;
    	struct CListNode* prev;
    	CLTDataType data;
    }CLTNode;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    其中prev是前一个结点地址,next是下一个结点。


    2.1、动态开辟一个空间

    代码如下(示例):

    CLTNode* BuyListNode(CLTDataType x)
    {
    	CLTNode* newnode = (CLTNode*)malloc(sizeof(CLTNode));
    	if (newnode == NULL)
    	{
    		perror("malloc fail");
    		exit(-1);
    	}
    	newnode->data = x;
    	newnode->next = NULL;
    	newnode->prev = NULL;
    	return newnode;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.2、初始化与打印

    1、初始化

    代码如下(示例):

    CLTNode* LTInit()
    {
    	CLTNode* phead = BuyListNode(-1);
    	phead->next = phead;
    	phead->prev = phead;
    
    	return phead;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2、打印

    void LTPrint(CLTNode* phead)
    {
    	assert(phead);
    
    	CLTNode* cur = phead->next;
    	while (cur != phead)
    	{
    		printf("%d ", cur->data);
    		cur = cur->next;
    	}
    	printf("\n");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.3、插入操作

    尾插

    在这里插入图片描述

    void LTPushBack(CLTNode* phead, CLTDataType x)
    {
    	assert(phead);
    
    	CLTNode* newnode = BuyListNode(x);
    	CLTNode* tail = phead->prev;
    
    	tail->next = newnode;
    	newnode->prev = tail;
    	newnode->next = phead;
    	phead->prev = newnode;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    头插

    头插一共两种写法

    第一种:
    在这里插入图片描述

    先让新结点的next指向下一个结点,再让头结点的next指向新结点,有先后顺序

    代码如下:

    void LTPushFront(CLTNode* phead, CLTDataType x)
    {
    	assert(phead);
    
    	CLTNode* newnode = BuyListNode(x);
    	newnode->next = phead->next;
    	phead->next->prev = newnode;
    
    	phead->next = newnode;
    	newnode->prev = phead;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    第二种:

    创建一个新指针,无先后顺序

    代码如下:

    void LTPushFront(CLTNode* phead, CLTDataType x)
    {
    	assert(phead);
    	CLTNode* newnode = BuyListNode(x);
    	CLTNode* first = phead->next;
    	phead->next = newnode;
    	newnode->prev = phead;
    	newnode->next = first;
    	first->prev = newnode;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在pos之前插入

    pos在head处时,进行尾插
    在这里插入图片描述
    pos在d1处时,进行头插

    代码如下:

    void LTInsert(CLTNode* pos, CLTDataType x)
    {
    	assert(pos);
    	CLTNode* prev = pos->prev;
    	CLTNode* newnode = BuyListNode(x);
    	prev->next = newnode;
    	newnode->prev = prev;
    	newnode->next = pos;
    	pos->prev = newnode;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在头删和尾删也可直接调用此函数
    头删:LTInsert(phead, x)
    尾删:LTInsert(phead->next, x)


    2.4、删除操作

    尾删

    void LTPopBack(CLTNode* phead)
    {
    	assert(phead);
    	assert(phead->next != phead);
    	CLTNode* tail = phead->prev;
    	CLTNode* tailPrev = tail->prev;
    
    	tailPrev->next = phead;
    	phead->prev = tailPrev;
    	free(tail);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    头删

    在这里插入图片描述

    代码如下:

    void LTPopFront(CLTNode* phead)
    {
    	assert(phead);
    	assert(phead->next != phead);
    	CLTNode* first = phead->next;
    	CLTNode* second = first->next;
    	free(first);
    	phead->next = second;
    	second->prev = phead;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    删除pos处的位置

    在这里插入图片描述
    代码如下:

    void LTErase(CLTNode* pos)
    {
    	assert(pos);
    	CLTNode* prev = pos->prev;
    	CLTNode* next = pos->next;
    	free(pos);
    	prev->next = next;
    	next->prev = prev;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在头删和尾删也可直接调用此函数
    头删:LTErase(phead->next)
    尾删:LTErase(phead->prev)


    2.4、查找与销毁操作

    1、查找

    代码如下:

    CLTNode* LTFind(CLTNode* phead, CLTDataType x)
    {
    	assert(phead);
    	CLTNode* cur = phead->next;
    	while (cur != phead)
    	{
    		if (cur->data == x)
    		{
    			return cur;
    		}
    
    		cur = cur->next;
    	}
    	return NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2、销毁

    代码如下:

    void LTDestroy(CLTNode* phead)
    {
    	assert(phead);
    	CLTNode* cur = phead->next;
    	while (cur != phead)
    	{
    		CLTNode* next = cur->next;
    		free(cur);
    
    		cur = next;
    	}
    	free(phead);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    vue的生命周期的相关问题
    关于人力外包公司那些事
    2023.10.19 关于设计模式 —— 单例模式
    vulhub中GitLab 远程命令执行漏洞复现(CVE-2021-22205)
    【QT+QGIS跨平台编译】之五十三:【QGIS_CORE跨平台编译】—【qgssqlstatementparser.cpp生成】
    MPLS虚拟专用网--跨域OptionC方案
    Azure Data Factory(八)数据集验证之服务主体(Service Principal)
    C与C++字符串方法示例
    记一次dump文件分析历程
    java解析html代码,采集网页信息
  • 原文地址:https://blog.csdn.net/Hush_H/article/details/127781895