数据结构_链表_单向循环链表 双向链表的初始化、插入、删除、修改、查询打印(基于C语言实现)

news/2024/11/7 20:43:21/文章来源:https://www.cnblogs.com/wyfm/p/18533925

一、单向循环链表的原理与应用

思考:对于单向链表而言,想要遍历链表,则必须从链表的首结点开始进行遍历,请问有没有更简单的方案实现链表中的数据的增删改查?

回答:是有的,可以使用单向循环的链表进行设计,单向循环的链表的使用规则和普通的单向链表没有较大的区别,需要注意:单向循环链表的尾结点的指针域中必须指向链表的首结点的地址,由于带头结点的单向循环链表更加容易进行管理,所以教学以带头结点的为例:

img

上图所示的就是一个典型的单向循环链表的结构,可以发现单向循环链表的结构属于环形结构,链表中的最后一个结点的指针域中存储的是链表的第一个结点的地址。

img

为了管理单向循环链表,需要构造头结点的数据类型以及构造有效结点的数据类型,如下:

img

(1) 创建一个空链表,由于是使用头结点,所以就需要申请头结点的堆内存并初始化即可!

img

(2) 创建新结点,为新结点申请堆内存并对新结点的数据域和指针域进行初始化,操作如下:

img

(3) 根据情况把新结点插入到链表中,此时可以分为尾部插入、头部插入、指定位置插入:
  • 头插

img

  • 尾插

img

  • 中插

img

(4) 根据情况可以从链表中删除某结点,此时可以分为尾部删除、头部删除、指定元素删除:
  • 头删

img

  • 尾删

img

  • 中删

img

代码

/*** @file name : CircularLinkedList.c* @brief     : 实现单向循环链表的相关功能* @author    :yfm3262@163.com* @date      :2024/11/07* @version   :1.0* @note      :* CopyRight (c)  2023-2024   yfm3262@163.com   All Right Reseverd*/

单向循环链表公式

img

/*** 声明单循环链表的结点** 单向循环链表总结成公式*     struct xxx*     {*         //数据域(需要存放什么类型的数据,你就定义对应的变量即可)*         //指针域(直接后继的指针域)*     };*   单向循环链表的基本操作:*     初始化单向循环链表 √*     插入数据 √*     删除数据 √*     修改数据*     查询打印数据√*/

初始化单向循环链表

构建单向循环链表结点

CircLList_t[ data |*next ]
// 指的是单向循环链表中的结点有效数据类型,用户可以根据需要进行修改
typedef int DataType_t;// 构造单向循环链表的结点,链表中所有结点的数据类型应该是相同的
typedef struct CircularLinkedList
{DataType_t data;                 // 结点的数据域struct CircularLinkedList *next; // 直接后继的指针域
} CircLList_t;

创建一个空链表(仅头结点)

img

/*** @name       CircLList_Create* @brief     创建一个空单向循环链表,仅含头结点,并对链表进行初始化* @param* @return*      @retval    Head 头结点地址* @date      2024/11/07* @version   1.0* @note*/
CircLList_t *CircLList_Create(void)
{// 1.创建一个头结点并对头结点申请内存CircLList_t *Head = (CircLList_t *)calloc(1, sizeof(CircLList_t));if (NULL == Head){perror("Calloc memory for Head is Failed");exit(-1);}// 2.对头结点进行初始化,头结点是不存储数据域,指针域指向自己, 体现循环的思想 [date|*next]Head->next = Head;// 3.把头结点的地址返回即可   Head-->[date|*next]return Head;
}

创建一个新结点

image

/*** @name       CircLList_NewNode* @brief     创建一个新的结点,并对新结点进行初始化(数据域 + 指针域)* @param     data 要创建结点的元素* @return    程序执行成功与否*      @retval    NULL 申请堆内存失败*      @retval    New  新结点地址* @date      2024/11/07* @version   1.0* @note*/
CircLList_t *CircLList_NewNode(DataType_t data)
{// 1.创建一个新结点并对新结点申请内存CircLList_t *New = (CircLList_t *)calloc(1, sizeof(CircLList_t));if (NULL == New){perror("Calloc memory for NewNode is Failed");return NULL;}// 2.对新结点的数据域和指针域进行初始化New->data = data;New->next = NULL;return New;
}

插入数据

头插

image

/*** @name       CircLList_HeadInsert* @brief     在单向循环链表的头结点后插入* @param     Head 头指针* @param     data 新元素* @return 程序执行成功与否*      @retval    false 插入失败*      @retval    true  插入成功* @date      2024/04/24* @version   1.0* @note*/
bool CircLList_HeadInsert(CircLList_t *Head, DataType_t data)
{// 备份头指针, 创建操作指针CircLList_t *Current = Head;// 1.创建新结点并对新结点进行初始化CircLList_t *New = CircLList_NewNode(data);if (NULL == New){printf("can not insert new node , Failed to create a node\n");return false;}// 2.判断单向循环链表是否为空,如果为空,则直接插入到头结点之后, 新结点作为首结点, 体现循环if (Head == Head->next){Head->next = New; // 让头结点的next指针指向新结点New->next = New;  // 新节点指向自己, 体现循环, 仅有一个首结点的单循环链表return true;}// 3.如果单向循环链表为非空,需要让尾结点的next指针指向首结点while (Current->next) // 不断向下检查结点指针域{Current = Current->next;         // 进入下一个结点if (Current->next == Head->next) // 结束条件: 达尾结点{break;}} // Current到达未尾结点Current->next = New;    // 尾结点指针域指向新的首结点New->next = Head->next; // 新结点链接旧首结点Head->next = New;       // 头结点的next指针域指向新结点的地址return true;
}

中插

/*** @name       CircLList_DestInsert* @brief     单向循环链表中的指定元素后面插入新结点* @param     Head 头指针* @param     dest 要查找的结点* @param     data 要插入的数据* @return 程序执行成功与否*      @retval    false 插入失败*      @retval    true  插入成功* @date      2024/11/07* @version   1.1* @note*/
bool CircLList_DestInsert(CircLList_t *Head, DataType_t dest, DataType_t data)
{CircLList_t *Current = Head->next; // 操作指针 初始为指向首结点, 若为空链表则指向头结点// 1.创建新结点并对新结点进行初始化CircLList_t *New = CircLList_NewNode(data);if (NULL == New){printf("can not insert new node , Failed to create a node\n");return false;}// 2.判断单向循环链表是否为空,如果为空,则新结点作为首结点, 体现循环if (Head == Head->next){Head->next = New; // 让头结点的next指针指向新结点New->next = New;  // 新节点指向自己, 体现循环, 单有效结点return true;}// 3.若单向循环链表非空,需要让尾结点的next指针指向新结点,新结点指向首结点// 目标结点是首结点, 不再继续查找. 目标结点非首结点, 继续向下查找while (Current->data != dest){Current = Current->next;                                      // 进入下一个结点if ((Current->next == Head->next) && (Current->data != dest)) // 达到末尾 且 末尾不是目标{printf("The target doesn't exist! \n");return false;}} // 找到目标结点, Current此时指向目标// 目标结点是首结点if (Current == Head->next){New->next = Current->next; // 新结点链接目标结点直接后继Current->next = New;}else if (Current->next == Head->next) // 目标结点是尾结点{New->next = Head->next; // 作为新尾结点Current->next = New;}else // 目标结点是中间结点{New->next = Current->next; // 新结点链接目标结点直接后继Current->next = New;       // 目标结点的直接后继更新为新结点}return true;
}

尾插

image

/*** @name      CircLList_TailInsert* @brief     将新元素插入到尾结点后面* @param     Head 头指针* @param     data 新元素* @return 程序执行成功与否*      @retval    false 插入失败*      @retval    true  插入成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_TailInsert(CircLList_t *Head, DataType_t data)
{CircLList_t *Phead = Head; // 备份头结点地址,防止头结点丢失// 1.创建新结点并对新结点进行初始化CircLList_t *New = CircLList_NewNode(data);if (NULL == New){printf("can not insert new node , Failed to create a node\n");return false;}// 2.判断单向循环链表是否为空,如果为空,则新结点作为首结点, 体现循环if (Head == Head->next){Head->next = New; // 让头结点的next指针指向新结点New->next = New;  // 新节点指向自己, 体现循环return true;}// 3.如果单向循环链表为非空,需要让尾结点的next指针指向新结点,新结点指向首结点while (Phead->next) // 不断向下检查结点指针域{Phead = Phead->next;           // 进入下一个结点if (Phead->next == Head->next) // 当到达尾结点{break;}}Phead->next = New;      // 尾结点指针域 链接 新结点New->next = Head->next; // 新结点指针域 指向 首结点return true;
}

删除数据

头删

image

/*** @name       CircLList_HeadDel* @brief     删除头结点后面的一个结点* @param     Head 头指针* @return 程序执行成功与否*      @retval    false 删除失败*      @retval    true  删除成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_HeadDel(CircLList_t *Head)
{// 1.创建操作指针// 备份头结点地址,防止头结点丢失CircLList_t *Phead = Head;// 备份首结点, 用于操作CircLList_t *Temp = Head->next;// 2.判断单向循环链表是否为空链表,如果为空, 则退出if (Head == Head->next){printf("CircLList is Empty! \n");return false;}// 3.判断链表中是否只有首结点if (Head->next == Head->next->next){Temp->next = NULL; // 首结点的指针域指向NULL, 且防止野指针和内存泄漏Head->next = Head; // 头结点next指针指向头结点, 体现"循环"free(Temp);        // 释放结点内存, 防止内存泄漏return true;}// 3.如果单向循环链表为非空,需要让尾结点的next指针指向新的首结点(原首结点的直接后继)while (Phead->next) // 不断向下检查结点指针域{Phead = Phead->next;           // 进入下一个结点if (Phead->next == Head->next) // 当到达尾结点{break;}}Phead->next = Head->next->next; // 让尾结点的next指针指向新的首结点(原首结点的直接后继)Head->next = Phead->next;       // 头结点的指针域 修改链接为 新的首结点Temp->next = NULL;              // 让旧的首结点的指针域指向NULL, 从链表中断开, 且防止野指针和内存泄漏free(Temp);                     // 释放旧首结点的内存, 防止内存泄漏return true;
}

中删

image

/*** @name       CircLList_DestDel* @brief     中删, 删除某个元素结点* @param     Head 头指针* @param     dest 要删除的目标元素* @return 程序执行成功与否*      @retval    false 删除失败, 未找到目标元素结点*      @retval    true  删除成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_DestDel(CircLList_t *Head, DataType_t dest)
{CircLList_t *Current = Head->next; // 操作指针 初始为指向首结点, 若为空链表则指向头结点CircLList_t *Prev = Head;          // 操作指针 存放当前操作指针的前一个结点地址// 1.判断单向循环链表是否为空,如果为空,则报错if (Head == Head->next){printf("Error,  CircularLinkList is empty! \n");return false;}// 2.若单向循环链表非空// 目标结点是首结点, 不再继续查找. 目标结点非首结点, 继续向下查找while (Current->data != dest){Prev = Current;                                               // 备份Current的前一个位置Current = Current->next;                                      // 进入下一个结点if ((Current->next == Head->next) && (Current->data != dest)) // 达到末尾 且 末尾不是目标{printf("The target doesn't exist! \n");return false;}} // 找到目标结点, Current此时指向目标  Prev为Current 的前一个位置// 目标结点是首结点if (Current == Head->next){// 若链表只有首结点if (Current->next == Head->next){Head->next = Head; // 空链表状态Current->next = NULL;free(Current); // 防止内存泄漏return true;}while (Prev->next) // 不断向下检查结点指针域{Prev = Prev->next;            // 进入下一个结点if (Prev->next == Head->next) // 结束条件: 达尾结点{break;}} // Prev到达未尾结点Prev->next = Current->next; // 更新尾结点指针域为新首结点地址Head->next = Current->next; // 更新首结点链接新首结点}else if (Current->next == Head->next) // 目标结点是尾结点{Prev->next = Head->next; // 新尾结点链接首结点, 行成循环}else // 目标结点是中间结点{Prev->next = Current->next;}Current->next = NULL;free(Current); // 防止内存泄漏return true;
}

尾删

image

/*** @name      CircLList_TailDel* @brief     删除尾结点* @param     Head 头指针* @return 程序执行成功与否*      @retval    false 删除失败, 链表为空*      @retval    true  删除成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_TailDel(CircLList_t *Head)
{CircLList_t *Current = Head->next; // 操作指针 初始为指向首结点, 若为空链表则指向头结点CircLList_t *Prev = Head;          // 操作指针 存放当前操作指针的前一个结点地址// 1.判断单向循环链表是否为空,如果为空,则报错if (Head == Head->next){printf("Error,  CircularLinkList is empty! \n");return false;}// 2.若单向循环链表非空// 若链表只有首结点if (Current->next == Head->next){Head->next = Head; // 空链表状态Current->next = NULL;free(Current); // 防止内存泄漏return true;}// 若还有别的结点while (Current->next) // 不断向下检查结点指针域{Prev = Current;Current = Current->next;         // 进入下一个结点if (Current->next == Head->next) // 结束条件: 达尾结点{break;}} // Current到达未尾结点 Prev为Current 的前一个位置Prev->next = Head->next; // 新尾结点链接首结点, 行成循环Current->next = NULL;free(Current); // 防止内存泄漏return true;
}

查询打印数据

遍历打印

/*** @name      CircLList_Print* @brief     从头到尾遍历链表* @param     Head 头指针* @return    无* @date      2024/11/07* @version   1.0* @note*/
void CircLList_Print(CircLList_t *Head)
{// 判断是否为空链表if (Head->next == Head){printf("The list is empty.\n");return;}CircLList_t *Current = Head->next; // 指向首结点printf("Circular Linked List: ");while (Current->next) // 不断向下检查结点指针域{printf(" -> %d", Current->data); // 打印结点数据if (Current->next == Head->next) // 结束条件: 达尾结点{break;}Current = Current->next; // 进入下一个结点}printf("\n"); // 刷新行缓冲, 输出缓冲区
}

测试

#include "CircularLinkedList.h"int main(int argc, char const *argv[])
{// 创建单向循环链表, 空链表CircLList_t *Manager = CircLList_Create();// 头插法 向链表中插入新结点printf("*********************************CircLList_HeadInsert********************************\n");CircLList_HeadInsert(Manager, 7);CircLList_HeadInsert(Manager, 4);CircLList_HeadInsert(Manager, 1);CircLList_HeadInsert(Manager, 8);CircLList_HeadInsert(Manager, 5);CircLList_HeadInsert(Manager, 2);CircLList_Print(Manager);/*Circular Linked List:  -> 2 -> 5 -> 8 -> 1 -> 4 -> 7*/// 中插法 向链表中插入新结点printf("*********************************CircLList_DestInsert********************************\n");CircLList_DestInsert(Manager, 7, 9);CircLList_DestInsert(Manager, 4, 6);CircLList_DestInsert(Manager, 2, 3);CircLList_DestInsert(Manager, 5, 10);CircLList_Print(Manager);/*Circular Linked List:  -> 2 -> 3 -> 5 -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9*/// 尾插法 向链表中插入新结点printf("*********************************CircLList_TailInsert********************************\n");CircLList_TailInsert(Manager, 13);CircLList_TailInsert(Manager, 12);CircLList_TailInsert(Manager, 11);CircLList_Print(Manager);/*Circular Linked List:  -> 2 -> 3 -> 5 -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9 -> 13 -> 12 -> 11*/// 头删法 删除首结点printf("*********************************CircLList_HeadDel********************************\n");CircLList_HeadDel(Manager);CircLList_HeadDel(Manager);CircLList_HeadDel(Manager);CircLList_Print(Manager);/*Circular Linked List:  -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9 -> 13 -> 12 -> 11*/// 中删法 删除指定结点printf("*********************************CircLList_DestDel********************************\n");CircLList_DestDel(Manager, 10);CircLList_DestDel(Manager, 1);CircLList_DestDel(Manager, 6);CircLList_DestDel(Manager, 11);CircLList_Print(Manager);/*Circular Linked List:  -> 8 -> 4 -> 7 -> 9 -> 13 -> 12*/// 尾删法 删除尾结点printf("*********************************CircLList_HeadDel********************************\n");CircLList_TailDel(Manager);CircLList_TailDel(Manager);CircLList_TailDel(Manager);CircLList_TailDel(Manager);CircLList_Print(Manager);/*Circular Linked List:  -> 8 -> 4*/CircLList_TailDel(Manager);CircLList_TailDel(Manager);CircLList_TailDel(Manager);/*Error,  CircularLinkList is empty! */CircLList_HeadInsert(Manager, 66);CircLList_Print(Manager);/*Circular Linked List:  -> 66*/// 等待用户响应printf("***Press any key to exit the test***\n");getchar();return 0;
}

测试结果:

*********************************CircLList_HeadInsert********************************
Circular Linked List:  -> 2 -> 5 -> 8 -> 1 -> 4 -> 7
*********************************CircLList_DestInsert********************************
Circular Linked List:  -> 2 -> 3 -> 5 -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9
*********************************CircLList_TailInsert********************************
Circular Linked List:  -> 2 -> 3 -> 5 -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9 -> 13 -> 12 -> 11
*********************************CircLList_HeadDel********************************
Circular Linked List:  -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9 -> 13 -> 12 -> 11
*********************************CircLList_DestDel********************************
Circular Linked List:  -> 8 -> 4 -> 7 -> 9 -> 13 -> 12
*********************************CircLList_HeadDel********************************
Circular Linked List:  -> 8 -> 4
Error,  CircularLinkList is empty! 
Circular Linked List:  -> 66
***Press any key to exit the test***

完整代码

CircularLinkedList.h

#ifndef __CIRCULARLINKEDLIST_H // ifndef是(如果 没有 定义 那么) (__该头文件的名称)
#define __CIRCULARLINKEDLIST_H // #define是 进行定义
/*** @file name : CircularLinkedList.c* @brief     : 实现单向循环链表的相关功能* @author    :yfm3262@163.com* @date      :2024/11/07* @version   :1.0* @note      :* CopyRight (c)  2023-2024   yfm3262@163.com   All Right Reseverd*/#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>/*** 声明单循环链表的结点** 单向循环链表总结成公式*     struct xxx*     {*         //数据域(需要存放什么类型的数据,你就定义对应的变量即可)*         //指针域(直接后继的指针域)*     };*   单向循环链表的基本操作:*     初始化单向循环链表 √*     插入数据 √*     删除数据 √*     修改数据*     查询打印数据√*/// 指的是单向循环链表中的结点有效数据类型,用户可以根据需要进行修改
typedef int DataType_t;// 构造单向循环链表的结点,链表中所有结点的数据类型应该是相同的
typedef struct CircularLinkedList
{DataType_t data;                 // 结点的数据域struct CircularLinkedList *next; // 直接后继的指针域
} CircLList_t;/*** @name       CircLList_Create* @brief     创建一个空单向循环链表,仅含头结点,并对链表进行初始化* @param* @return*      @retval    Head 头结点地址* @date      2024/11/07* @version   1.0* @note*/
CircLList_t *CircLList_Create(void);/*** @name       CircLList_NewNode* @brief     创建一个新的结点,并对新结点进行初始化(数据域 + 指针域)* @param     data 要创建结点的元素* @return    程序执行成功与否*      @retval    NULL 申请堆内存失败*      @retval    New  新结点地址* @date      2024/11/07* @version   1.0* @note*/
CircLList_t *CircLList_NewNode(DataType_t data);/*** @name       CircLList_HeadInsert* @brief     在单向循环链表的头结点后插入* @param     Head 头指针* @param     data 新元素* @return 程序执行成功与否*      @retval    false 插入失败*      @retval    true  插入成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_HeadInsert(CircLList_t *Head, DataType_t data);/*** @name       CircLList_DestInsert* @brief     单向循环中的指定元素后面插入新结点* @param     Head 头指针* @param     dest 要查找的结点* @param     data 要插入的数据* @return 程序执行成功与否*      @retval    false 插入失败*      @retval    true  插入成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_DestInsert(CircLList_t *Head, DataType_t dest, DataType_t data);/*** @name      CircLList_TailInsert* @brief     将新元素插入到尾结点后面* @param     Head 头指针* @param     data 新元素* @return 程序执行成功与否*      @retval    false 插入失败*      @retval    true  插入成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_TailInsert(CircLList_t *Head, DataType_t data);/*** @name       CircLList_HeadDel* @brief     删除头结点后面的一个结点* @param     Head 头指针* @return 程序执行成功与否*      @retval    false 删除失败*      @retval    true  删除成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_HeadDel(CircLList_t *Head);/*** @name       CircLList_DestDel* @brief     中删, 删除某个元素结点* @param     Head 头指针* @param     dest 要删除的目标元素* @return 程序执行成功与否*      @retval    false 删除失败, 未找到目标元素结点*      @retval    true  删除成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_DestDel(CircLList_t *Head, DataType_t dest);/*** @name      CircLList_TailDel* @brief     删除尾结点* @param     Head 头指针* @return 程序执行成功与否*      @retval    false 删除失败, 链表为空*      @retval    true  删除成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_TailDel(CircLList_t *Head);
/*** @name      CircLList_Print* @brief     从头到尾遍历链表* @param     Head 头指针* @return    无* @date      2024/11/07* @version   1.0* @note*/
void CircLList_Print(CircLList_t *Head);
#endif
// 结束定义

CircularLinkedList.c

/*** @file name : CircularLinkedList.c* @brief     : 实现单向循环链表的相关功能* @author    :yfm3262@163.com* @date      :2024/11/07* @version   :1.0* @note      :* CopyRight (c)  2023-2024   yfm3262@163.com   All Right Reseverd*/
#include "CircularLinkedList.h"/*** @name       CircLList_Create* @brief     创建一个空单向循环链表,仅含头结点,并对链表进行初始化* @param* @return*      @retval    Head 头结点地址* @date      2024/11/07* @version   1.0* @note*/
CircLList_t *CircLList_Create(void)
{// 1.创建一个头结点并对头结点申请内存CircLList_t *Head = (CircLList_t *)calloc(1, sizeof(CircLList_t));if (NULL == Head){perror("Calloc memory for Head is Failed");exit(-1);}// 2.对头结点进行初始化,头结点是不存储数据域,指针域指向自己, 体现循环的思想 [date|*next]Head->next = Head;// 3.把头结点的地址返回即可   Head-->[date|*next]return Head;
}/*** @name       CircLList_NewNode* @brief     创建一个新的结点,并对新结点进行初始化(数据域 + 指针域)* @param     data 要创建结点的元素* @return    程序执行成功与否*      @retval    NULL 申请堆内存失败*      @retval    New  新结点地址* @date      2024/11/07* @version   1.0* @note*/
CircLList_t *CircLList_NewNode(DataType_t data)
{// 1.创建一个新结点并对新结点申请内存CircLList_t *New = (CircLList_t *)calloc(1, sizeof(CircLList_t));if (NULL == New){perror("Calloc memory for NewNode is Failed");return NULL;}// 2.对新结点的数据域和指针域进行初始化New->data = data;New->next = NULL;return New;
}/*** @name       CircLList_HeadInsert* @brief     在单向循环链表的头结点后插入* @param     Head 头指针* @param     data 新元素* @return 程序执行成功与否*      @retval    false 插入失败*      @retval    true  插入成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_HeadInsert(CircLList_t *Head, DataType_t data)
{// 备份头指针, 创建操作指针CircLList_t *Current = Head;// 1.创建新结点并对新结点进行初始化CircLList_t *New = CircLList_NewNode(data);if (NULL == New){printf("can not insert new node , Failed to create a node\n");return false;}// 2.判断单向循环链表是否为空,如果为空,则直接插入到头结点之后, 新结点作为首结点, 体现循环if (Head == Head->next){Head->next = New; // 让头结点的next指针指向新结点New->next = New;  // 新节点指向自己, 体现循环, 仅有一个首结点的单循环链表return true;}// 3.如果单向循环链表为非空,需要让尾结点的next指针指向首结点while (Current->next) // 不断向下检查结点指针域{Current = Current->next;         // 进入下一个结点if (Current->next == Head->next) // 结束条件: 达尾结点{break;}} // Current到达未尾结点Current->next = New;    // 尾结点指针域指向新的首结点New->next = Head->next; // 新结点链接旧首结点Head->next = New;       // 头结点的next指针域指向新结点的地址return true;
}/*** @name       CircLList_DestInsert* @brief     单向循环链表中的指定元素后面插入新结点* @param     Head 头指针* @param     dest 要查找的结点* @param     data 要插入的数据* @return 程序执行成功与否*      @retval    false 插入失败*      @retval    true  插入成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_DestInsert(CircLList_t *Head, DataType_t dest, DataType_t data)
{CircLList_t *Current = Head->next; // 操作指针 初始为指向首结点, 若为空链表则指向头结点// 1.创建新结点并对新结点进行初始化CircLList_t *New = CircLList_NewNode(data);if (NULL == New){printf("can not insert new node , Failed to create a node\n");return false;}// 2.判断单向循环链表是否为空,如果为空,则新结点作为首结点, 体现循环if (Head == Head->next){Head->next = New; // 让头结点的next指针指向新结点New->next = New;  // 新节点指向自己, 体现循环, 单有效结点return true;}// 3.若单向循环链表非空,需要让尾结点的next指针指向新结点,新结点指向首结点// 目标结点是首结点, 不再继续查找. 目标结点非首结点, 继续向下查找while (Current->data != dest){Current = Current->next;                                      // 进入下一个结点if ((Current->next == Head->next) && (Current->data != dest)) // 达到末尾 且 末尾不是目标{printf("The target doesn't exist! \n");return false;}} // 找到目标结点, Current此时指向目标// 目标结点是首结点if (Current == Head->next){New->next = Current->next; // 新结点链接目标结点直接后继Current->next = New;}else if (Current->next == Head->next) // 目标结点是尾结点{New->next = Head->next; // 作为新尾结点Current->next = New;}else // 目标结点是中间结点{New->next = Current->next; // 新结点链接目标结点直接后继Current->next = New;       // 目标结点的直接后继更新为新结点}return true;
}/*** @name      CircLList_TailInsert* @brief     将新元素插入到尾结点后面* @param     Head 头指针* @param     data 新元素* @return 程序执行成功与否*      @retval    false 插入失败*      @retval    true  插入成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_TailInsert(CircLList_t *Head, DataType_t data)
{CircLList_t *Phead = Head; // 备份头结点地址,防止头结点丢失// 1.创建新结点并对新结点进行初始化CircLList_t *New = CircLList_NewNode(data);if (NULL == New){printf("can not insert new node , Failed to create a node\n");return false;}// 2.判断单向循环链表是否为空,如果为空,则新结点作为首结点, 体现循环if (Head == Head->next){Head->next = New; // 让头结点的next指针指向新结点New->next = New;  // 新节点指向自己, 体现循环return true;}// 3.如果单向循环链表为非空,需要让尾结点的next指针指向新结点,新结点指向首结点while (Phead->next) // 不断向下检查结点指针域{Phead = Phead->next;           // 进入下一个结点if (Phead->next == Head->next) // 当到达尾结点{break;}}Phead->next = New;      // 尾结点指针域 链接 新结点New->next = Head->next; // 新结点指针域 指向 首结点return true;
}/*** @name       CircLList_HeadDel* @brief     删除头结点后面的一个结点* @param     Head 头指针* @return 程序执行成功与否*      @retval    false 删除失败*      @retval    true  删除成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_HeadDel(CircLList_t *Head)
{// 1.创建操作指针// 备份头结点地址,防止头结点丢失CircLList_t *Phead = Head;// 备份首结点, 用于操作CircLList_t *Temp = Head->next;// 2.判断单向循环链表是否为空链表,如果为空, 则退出if (Head == Head->next){printf("CircLList is Empty! \n");return false;}// 3.判断链表中是否只有首结点if (Head->next == Head->next->next){Temp->next = NULL; // 首结点的指针域指向NULL, 且防止野指针和内存泄漏Head->next = Head; // 头结点next指针指向头结点, 体现"循环"free(Temp);        // 释放结点内存, 防止内存泄漏return true;}// 3.如果单向循环链表为非空,需要让尾结点的next指针指向新的首结点(原首结点的直接后继)while (Phead->next) // 不断向下检查结点指针域{Phead = Phead->next;           // 进入下一个结点if (Phead->next == Head->next) // 当到达尾结点{break;}}Phead->next = Head->next->next; // 让尾结点的next指针指向新的首结点(原首结点的直接后继)Head->next = Phead->next;       // 头结点的指针域 修改链接为 新的首结点Temp->next = NULL;              // 让旧的首结点的指针域指向NULL, 从链表中断开, 且防止野指针和内存泄漏free(Temp);                     // 释放旧首结点的内存, 防止内存泄漏return true;
}/*** @name       CircLList_DestDel* @brief     中删, 删除某个元素结点* @param     Head 头指针* @param     dest 要删除的目标元素* @return 程序执行成功与否*      @retval    false 删除失败, 未找到目标元素结点*      @retval    true  删除成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_DestDel(CircLList_t *Head, DataType_t dest)
{CircLList_t *Current = Head->next; // 操作指针 初始为指向首结点, 若为空链表则指向头结点CircLList_t *Prev = Head;          // 操作指针 存放当前操作指针的前一个结点地址// 1.判断单向循环链表是否为空,如果为空,则报错if (Head == Head->next){printf("Error,  CircularLinkList is empty! \n");return false;}// 2.若单向循环链表非空// 目标结点是首结点, 不再继续查找. 目标结点非首结点, 继续向下查找while (Current->data != dest){Prev = Current;                                               // 备份Current的前一个位置Current = Current->next;                                      // 进入下一个结点if ((Current->next == Head->next) && (Current->data != dest)) // 达到末尾 且 末尾不是目标{printf("The target doesn't exist! \n");return false;}} // 找到目标结点, Current此时指向目标  Prev为Current 的前一个位置// 目标结点是首结点if (Current == Head->next){// 若链表只有首结点if (Current->next == Head->next){Head->next = Head; // 空链表状态Current->next = NULL;free(Current); // 防止内存泄漏return true;}while (Prev->next) // 不断向下检查结点指针域{Prev = Prev->next;            // 进入下一个结点if (Prev->next == Head->next) // 结束条件: 达尾结点{break;}} // Prev到达未尾结点Prev->next = Current->next; // 更新尾结点指针域为新首结点地址Head->next = Current->next; // 更新首结点链接新首结点}else if (Current->next == Head->next) // 目标结点是尾结点{Prev->next = Head->next; // 新尾结点链接首结点, 行成循环}else // 目标结点是中间结点{Prev->next = Current->next;}Current->next = NULL;free(Current); // 防止内存泄漏return true;
}/*** @name      CircLList_TailDel* @brief     删除尾结点* @param     Head 头指针* @return 程序执行成功与否*      @retval    false 删除失败, 链表为空*      @retval    true  删除成功* @date      2024/11/07* @version   1.0* @note*/
bool CircLList_TailDel(CircLList_t *Head)
{CircLList_t *Current = Head->next; // 操作指针 初始为指向首结点, 若为空链表则指向头结点CircLList_t *Prev = Head;          // 操作指针 存放当前操作指针的前一个结点地址// 1.判断单向循环链表是否为空,如果为空,则报错if (Head == Head->next){printf("Error,  CircularLinkList is empty! \n");return false;}// 2.若单向循环链表非空// 若链表只有首结点if (Current->next == Head->next){Head->next = Head; // 空链表状态Current->next = NULL;free(Current); // 防止内存泄漏return true;}// 若还有别的结点while (Current->next) // 不断向下检查结点指针域{Prev = Current;Current = Current->next;         // 进入下一个结点if (Current->next == Head->next) // 结束条件: 达尾结点{break;}} // Current到达未尾结点 Prev为Current 的前一个位置Prev->next = Head->next; // 新尾结点链接首结点, 行成循环Current->next = NULL;free(Current); // 防止内存泄漏return true;
}
/*** @name      CircLList_Print* @brief     从头到尾遍历链表* @param     Head 头指针* @return    无* @date      2024/11/07* @version   1.0* @note*/
void CircLList_Print(CircLList_t *Head)
{// 判断是否为空链表if (Head->next == Head){printf("The list is empty.\n");return;}CircLList_t *Current = Head->next; // 指向首结点printf("Circular Linked List: ");while (Current->next) // 不断向下检查结点指针域{printf(" -> %d", Current->data); // 打印结点数据if (Current->next == Head->next) // 结束条件: 达尾结点{break;}Current = Current->next; // 进入下一个结点}printf("\n"); // 刷新行缓冲, 输出缓冲区
}

projecttesting.c

/*** @file name : projecttesting.c* @brief     : 实现单向循环链表的相关功能测试* @author    :yfm3262@163.com* @date      :2024/11/07* @version   :1.0* @note      :* CopyRight (c)  2023-2024   yfm3262@163.com   All Right Reseverd*/#include "CircularLinkedList.h"int main(int argc, char const *argv[])
{// 创建单向循环链表, 空链表CircLList_t *Manager = CircLList_Create();// 头插法 向链表中插入新结点printf("*********************************CircLList_HeadInsert********************************\n");CircLList_HeadInsert(Manager, 7);CircLList_HeadInsert(Manager, 4);CircLList_HeadInsert(Manager, 1);CircLList_HeadInsert(Manager, 8);CircLList_HeadInsert(Manager, 5);CircLList_HeadInsert(Manager, 2);CircLList_Print(Manager);/*Circular Linked List:  -> 2 -> 5 -> 8 -> 1 -> 4 -> 7*/// 中插法 向链表中插入新结点printf("*********************************CircLList_DestInsert********************************\n");CircLList_DestInsert(Manager, 7, 9);CircLList_DestInsert(Manager, 4, 6);CircLList_DestInsert(Manager, 2, 3);CircLList_DestInsert(Manager, 5, 10);CircLList_Print(Manager);/*Circular Linked List:  -> 2 -> 3 -> 5 -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9*/// 尾插法 向链表中插入新结点printf("*********************************CircLList_TailInsert********************************\n");CircLList_TailInsert(Manager, 13);CircLList_TailInsert(Manager, 12);CircLList_TailInsert(Manager, 11);CircLList_Print(Manager);/*Circular Linked List:  -> 2 -> 3 -> 5 -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9 -> 13 -> 12 -> 11*/// 头删法 删除首结点printf("*********************************CircLList_HeadDel********************************\n");CircLList_HeadDel(Manager);CircLList_HeadDel(Manager);CircLList_HeadDel(Manager);CircLList_Print(Manager);/*Circular Linked List:  -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9 -> 13 -> 12 -> 11*/// 中删法 删除指定结点printf("*********************************CircLList_DestDel********************************\n");CircLList_DestDel(Manager, 10);CircLList_DestDel(Manager, 1);CircLList_DestDel(Manager, 6);CircLList_DestDel(Manager, 11);CircLList_Print(Manager);/*Circular Linked List:  -> 8 -> 4 -> 7 -> 9 -> 13 -> 12*/// 尾删法 删除尾结点printf("*********************************CircLList_HeadDel********************************\n");CircLList_TailDel(Manager);CircLList_TailDel(Manager);CircLList_TailDel(Manager);CircLList_TailDel(Manager);CircLList_Print(Manager);/*Circular Linked List:  -> 8 -> 4*/CircLList_TailDel(Manager);CircLList_TailDel(Manager);CircLList_TailDel(Manager);/*Error,  CircularLinkList is empty! */CircLList_HeadInsert(Manager, 66);CircLList_Print(Manager);/*Circular Linked List:  -> 66*/// 等待用户响应printf("***Press any key to exit the test***\n");getchar();return 0;
}

二、双向链表的原理与应用

如果想要提高单向链表或者单向循环链表的访问速度,则可以在链表中的结点中再添加一个指针域,让新添加的指针域指向当前结点的直接前驱的地址,也就意味着一个结点中有两个指针域(prev + next),也被称为双向链表(Double Linked List)。

img

由于带头结点更加方便用户进行数据访问,所以本次创建一条带头结点的双向不循环的链表。

img

(1) 创建一个空链表,由于是使用头结点,所以就需要申请头结点的堆内存并初始化即可!

img

(2) 创建新结点,为新结点申请堆内存并对新结点的数据域和指针域进行初始化,操作如下:

img

(3) 根据情况可以从链表中插入新结点,此时可以分为尾部插入、头部插入、指定位置插入:
  • 头插

img

  • 尾插

img

  • 中插

img

(4) 根据情况可以从链表中删除某结点,此时可以分为尾部删除、头部删除、指定结点删除:
  • 头删

img

  • 尾删

img

  • 中删

img

代码

/*** @file name : 双向链表* @brief     : 双向链表的初始化、插入、删除、修改、查询打印* @author    : yfm3262@163.com* @date      : 2024/11/07* @version   : 1.0* @note      : 待续:修改数据、 查询打印数据 * CopyRight (c)  2023-2024   yfm3262@163.com   All Right Reseverd
*/

双向链表公式

/*双向链表总结成公式struct xxx{//指针域(直接前驱的指针域)//数据域(需要存放什么类型的数据,你就定义对应的变量即可)//指针域(直接后继的指针域)};双向链表的基本操作:初始化双向链表 √插入数据 √删除数据 √修改数据查询打印数据
*/

初始化双向链表

构建双向链表结点

image

DoubleLList_t [*prev | data |*next ]
// 指的是双向链表中的结点有效数据类型,用户可以根据需要进行修改
typedef int DataType_t;// 构造双向链表的结点,链表中所有结点的数据类型应该是相同的
typedef struct DoubleLinkedList
{struct DoubleLinkedList *prev; // 直接前驱的指针域DataType_t data;               // 结点的数据域struct DoubleLinkedList *next; // 直接后继的指针域} DoubleLList_t;

创建一个空链表(仅头结点)

image

/*** @name      DoubleLList_Create* @brief     创建一个空双向链表,空链表应该有一个头结点,对链表进行初始化* @param* @return*      @retval    Head 头结点地址* @date      2024/11/07* @version   1.0* @note*/
DoubleLList_t *DoubleLList_Create(void)
{// 1.创建一个头结点并对头结点申请内存DoubleLList_t *Head = (DoubleLList_t *)calloc(1, sizeof(DoubleLList_t));if (NULL == Head){perror("Calloc memory for Head is Failed");exit(-1);}// 2.对头结点进行初始化,头结点是不存储数据域,指针域指向NULL [prev|date|next]Head->prev = NULL;Head->next = NULL;// 3.把头结点的地址返回即可   Head-->[prev|date|next]return Head;
}

创建一个新结点

image

/*** @name      DoubleLList_NewNode* @brief     创建新的结点,并对新结点进行初始化(数据域 + 指针域)* @param     data 要创建结点的元素* @return    程序执行成功与否*      @retval    NULL 申请堆内存失败*      @retval    New  新结点地址* @date      2024/11/07* @version   1.0* @note*/
DoubleLList_t *DoubleLList_NewNode(DataType_t data)
{// 1.创建一个新结点并对新结点申请内存DoubleLList_t *New = (DoubleLList_t *)calloc(1, sizeof(DoubleLList_t));if (NULL == New){perror("Calloc memory for NewNode is Failed");return NULL;}// 2.对新结点的数据域和指针域(2个)进行初始化New->data = data;New->prev = NULL;New->next = NULL;return New;
}

插入数据

头插

image

/*** @name      DoubleLList_HeadInsert* @brief     在双向链表的头结点后插入* @param     Head 头指针* @param     data 新元素* @return 程序执行成功与否*      @retval    false 插入失败*      @retval    true  插入成功* @date      2024/11/07* @version   1.0* @note*/
bool DoubleLList_HeadInsert(DoublelList_t *Head, DataType_t data)
{// 1.创建新结点并对新结点进行初始化DoubleLList_t *New = DoubleLList_NewNode(data);if (NULL == New){printf("can not insert new node , Failed to create a node\n");return false;}// 2.判断双向链表是否为空,如果为空,则直接插入到头结点之后if (NULL == Head->next){Head->next = New; // 让头结点的next指针指向新结点return true;}// 3.如果双向链表为非空,则把新结点插入到链表的头部New->next = Head->next; // 新结点的next指针指向原本的首结点地址Head->next->prev = New; // 原本的首结点的prev指针指向新结点的地址Head->next = New;       // 更新头结点的next指针,让next指针指向新结点的地址return true;
}

中插

image

/*** @name      Doublellist_DestInsert* @brief     双向链表中的指定元素后面插入新结点* @param     Head 头指针* @param     dest 要查找的结点* @param     data 要插入的数据* @return 程序执行成功与否*      @retval    false 插入失败*      @retval    true  插入成功* @date      2024/11/07* @version   1.0* @note*/
bool Doublellist_DestInsert(DoubleLList_t *Head, DataType_t dest, DataType_t data)
{DoubleLList_t *Phead = Head; // 备份头结点地址,防止头结点丢失// 1.创建新结点并对新结点进行初始化DoubleLList_t *New = DoubleLList_NewNode(data);if (NULL == New){printf("can not insert new node , Failed to create a node\n");return false;}// 2.判断双向链表是否为空,如果为空,则直接插入到头结点之后if (NULL == Head->next){Head->next = New; // 让头结点的next指针指向新结点return true;}// 3.如果双向链表为非空,此时分为3种情况(尾部or中间)while (Phead->next){Phead = Phead->next;if (Phead->data == dest){break;}}// 如果遍历链表之后发现没有目标结点,则退出即可if (Phead->next == NULL && Phead->data != dest){printf("dest node is not found\n");return false;}// 如果遍历链表找到目标结点,则分为(尾部or中间)if (Phead->next == NULL) // 尾插{New->prev = Phead; // 新结点的prev指针指向尾结点的地址Phead->next = New; // 尾结点的next指针指向新结点}else // 中间{New->next = Phead->next; // 新结点的next指针指向目标结点的直接后继结点New->prev = Phead;       // 新结点的prev指针指向目标结点的地址Phead->next->prev = New; // 目标结点的直接后继结点的prev指针指向新结点Phead->next = New;       // 目标结点的next指针指向新结点}return true;
}

尾插

image

/*** @name      DoubleLList_TailInsert* @brief     将新元素插入到尾结点后面* @param     Head 头指针* @param     data 新元素* @return 程序执行成功与否*      @retval    false 插入失败*      @retval    true  插入成功* @date      2024/11/07* @version   1.0* @note*/
bool DoubleLList_TailInsert(DoubleLList_t *Head, DataType_t data)
{DoubleLList_t *Phead = Head; // 备份头结点地址,防止头结点丢失// 1.创建新结点并对新结点进行初始化DoubleLList_t *New = DoubleLList_NewNode(data);if (NULL == New){printf("can not insert new node , Failed to create a node\n");return false;}// 2,判断双向链表是否为空,如果为空,则直接插入到头结点之后if (NULL == Head->next){Head->next = New; // 让头结点的next指针指向新结点return true;}// 3.如果双向链表为非空,则把新结点插入到链表的尾部while (Phead->next){Phead = Phead->next;}Phead->next = New; // 尾结点的next指针指向新结点地址New->prev = Phead; // 新结点的prev指针指向原本的尾结点地址return true;
}

删除数据

头删

image

/*** @name      DoublelList_HeadDel* @brief     删除头结点后面的一个结点* @param     Head 头指针* @return 程序执行成功与否*      @retval    false 删除失败*      @retval    true  删除成功* @date      2024/11/07* @version   1.0* @note*/
bool DoublelList_HeadDel(DoubleLList_t *Head)
{// 检查链表是否为空或只有头结点if (Head == NULL || Head->next == NULL){return false;}DoubleLList_t *Phead = Head->next; // 指向头结点后的第一个结点// 如果头结点后还有更多结点if (Phead->next != NULL){Phead->next->prev = Head;}Head->next = Phead->next; // 更新头结点的next指针Phead->next = NULL;       // 防止野指针和内存泄漏// 清理节点free(Phead); // 释放内存return true;
}

中删

image

/*** @name      DoublelList_DestDel* @brief     中删, 删除某个元素结点* @param     Head 头指针* @param     dest 要删除的目标元素* @return 程序执行成功与否*      @retval    false 删除失败, 未找到目标元素结点*      @retval    true  删除成功* @date      2024/11/07* @version   1.0* @note*/
bool DoublelList_DestDel(DoubleLList_t *Head, DataType_t dest)
{DoubleLList_t *Phead = Head; // 备份头结点地址,防止头结点丢失// 1.判断双向链表是否为空, 如果为空, 则直接插入到头结点之后if (NULL == Head->next){printf("linked list is empty\n");return false;}// 2.如果双向链表为非空,此时遍历链表查找没有目标结点(找到or未找到)while (Phead->next){Phead = Phead->next;if (Phead->data = dest){break;}}// 如果链表中没有目标结点,此时直接退出即可if (Phead->next == NULL && Phead->data != dest){printf("dest node is not found\n");return false;}// 如果链表中发现目标结点,此时分为(头部 or尾部 or中间)if (Phead == Head->next) // 头部{Head->next = Phead->next; // 更新头结点,让头结点的next指针指向首结点的直接后继if (Phead->next != NULL){Phead->next->prev = NULL;Phead->next = NULL;}free(Phead); // 释放待删除结点内存}else if (Phead->next == NULL) // 尾部{Phead->prev->next = NULL; // 尾结点的直接前驱结点的next指针指向NULLPhead->prev = NULL;       // 尾结点的prev指针指向NULLfree(Phead);              // 释放待删除结点内存}else{Phead->prev->next = Phead->next; // 让待删除结点的直接前驱结点的next指针指向待删除结点的直接后继地址Phead->next->prev = Phead->prev; // 让待删除结点的直接后继结点的prev指针指向待删除结点的直接前驱地址Phead->next = NULL;              // 让待删除结点的next指针指向NULLPhead->prev = NULL;              // 让待删除结点的prev指针指向NULLfree(Phead);                     // 释放待删除结点内存}return true;
}

尾删

image

/*** @name      DoublelList_TailDel* @brief     删除尾结点* @param     Head 头指针* @return 程序执行成功与否*      @retval    false 删除失败, 链表为空*      @retval    true  删除成功* @date      2024/11/07* @version   1.0* @note*/
bool DoublelList_TailDel(DoubleLList_t *Head)
{// 创建操作指针DoubleLList_t *current = Head;// 检查链表是否为空或只有头结点if (Head == NULL || Head->next == NULL){return false;}// 遍历到尾结点while (current->next != NULL){current = current->next;}// current现在指向尾结点if (current->prev != NULL){current->prev->next = NULL; // 将尾结点的直接前驱设置为NULL}// 释放尾结点的内存, 防止内存泄漏free(current);return true;
}

其它

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
// 主函数,用于演示
int main(int argc, char *argv[]) {return 0;
}

练习:

img

img

img

img

img

img

img

img

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/828448.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

『模拟赛』多校A层冲刺NOIP2024模拟赛19

『模拟赛记录』多校A层冲刺NOIP2024模拟赛19Rank byd CSP 之后就没场切过题😡😡😡A. 图书管理 签,又寄了。 这种题直接做复杂度算着不对的话大概率就是要拆分贡献了。赛时用对顶堆维护的中位数,卡常到极致在 \(n=10^4\) 时要跑 1.2s。 感觉卡常有用所以写下来:发现如果…

WSL 挂载虚拟磁盘

为了扩展 WSL 虚拟机的大小,可以在 D 盘创建一个虚拟硬盘文件作为 WSL 虚拟机的数据盘。创建虚拟硬盘文件。打开磁盘工具,点击 操作 > 创建 VHD 打开虚拟硬盘创建菜单,创建一个虚拟硬盘文件:挂载虚拟硬盘。打开终端(管理员),运行下面的命令找到刚刚新建的虚拟磁盘: …

HTML - 1

HTML - 1 基础内容 标签与标签属性 属性不区分大小写 (推荐小写)可以用双引号 也可以用单引号 (推荐双引号)重复的属性,后边的会失效通用属性:id: 给标签打上唯一标识 (head html meta script style title不能加) ​ class:指定标签类名,与样式配合 ​ style:…

umount的时候target is busy

https://blog.csdn.net/u013409979/article/details/139867156

关于JVM调优与实践

1.如何开始JVM调优 ——tomcat内部署war包 修改TOMCAT_HOME/bin/catalina.sh文件JAVA_OPTS="-Xms512m -Xmx1024m"——linux环境下jar包启动springboot项目 启动时使用nohup java -Xms512m -Xmx1024m -jar x.jar --spring.profiles.active=prod &nohup:在系统后天…

为什么编号应该从 0 开始

在常见的编程语言如 Python、Go、Java 中,序列的下标都是从 0 开始的,为什么不是从 1 开始呢? 迪杰斯特拉在 1982 年的时候就思考过编号起点的问题,那个时候还没有上面这 3 门语言呢。大概思路如下:序列下标是连续的整数,首先要考虑的就是怎么用区间范围表示连续的整数,…

编写高质量代码(手撕代码)

首先上几个面试题:(真难)1. 手写函数实现数组扁平化(只减少一级嵌套)思路:function flatten(arr) {let res = [];arr.forEach((item) => {if (Array.isArray(item)) {item.forEach((e) => res.push(e));} else {res.push(item);}});return res;}console.log(flatte…

LeetCode LCR135[报数]

LeetCode LCR135[报数]题目 链接 LeetCode LCR135[报数] 详情实例题解 思路 通过 pow 函数对10进行幂运算,来获取报数范围 然后循环遍历 通过 push_back 方法将数字加入到容器内 代码 class Solution { public:vector<int> countNumbers(int cnt) {vector<int> iR…

语音 AI 迎来爆发期,也仍然隐藏着被低估的机会丨RTE2024 音频技术和 Voice AI 专场

在人工智能快速发展的今天,语音交互技术正经历一场革命性的变革。从语音识别到语音合成,再到端到端的语音对话系统,这一领域的创新正以前所未有的速度推进。这些进步不仅提升了技术指标,更为实时翻译、虚拟数字人、智能客服等实时互动场景带来了新的可能。本届 RTE2024 大会…

节能攻略,AUTOSAR PN局部网络管理技术!

随着整车功能的不断演进,车上各类用电设备(控制器、执行机构、感知设备等)的用电功耗越来越大,为了降低整车能耗,国内外很多OEM及Tire1都在考虑相关的机制及方案,其中PN局部网络管理机制,以其简单、灵活的特点获得众多落地应用。 随着整车功能的不断演进,车上各类…

AUTOSAR解决方案 — INTEWORK-EAS-CP

INTEWORK-EAS-CP(ECU AUTOSAR Software)是经纬恒润自主研发的符合AUTOSAR标准的软件产品。解决方案涵盖了嵌入式标准软件、AUTOSAR工具链、集成服务和培训等各个方面的内容,旨在为国内的OEM和供应商提供稳定可靠、便捷易用的AUTOSAR平台。 随着汽车电子技术的发展,汽…

多校A层冲刺NOIP2024模拟赛19

讲个笑话: (讨论时间) huge:(叹气)这讨论啊,就是改不了,这换了铃声了,也没…… 众人:现在是讨论时间啊。 huge:(停顿)那刚才大课间那会哇啦哇啦的…… 图书管理 简要题意 给定一个长度为\(n(n\le 10^4)\)的排列,求\(\sum\limits_{l=1}^n\sum\limits_{r=l}^n[r-l为…