目录
一、数据结构之单链表![](https://img-blog.csdnimg.cn/direct/288f52c993de4a179af9082d8297c5ff.png)
1.1 单链表的定义
- 链表是一种数据结构,其物理存储结构上是非连续、非顺序的存储结构。然而,在逻辑上,链表是连续的、顺序的,数据元素的逻辑顺序是通过链表中的指针连接次序来实现的。
- 链表中的节点通过指针相互连接,形成一个逻辑上的有序序列,尽管实际存储位置可能是分散的。这种特性使得链表能够动态地增加、删除节点,灵活地管理数据。
- 其中每个节点包含两部分信息:数据域data(存储数据)和指针域next(指向下一个节点)。单链表的最后一个节点指向空(NULL),表示链表的结束。
1.2 单链表与顺序表的优缺点对比
1.2.1 单链表的优缺点:
优点:
1. 插入和删除效率高:在单链表中,插入和删除节点的效率很高,只需修改指针指向即可,不需要移动大量元素。
2. 动态分配内存:单链表可以根据需要动态分配内存,灵活地管理数据结构的大小。3. 不需要预先分配内存空间:单链表不需要预先分配一定大小的内存空间,可以根据需求动态分配。
缺点:
1. 不支持随机访问:单链表不支持直接随机访问元素,需要从头节点开始逐个遍历。
1.2.2 顺序表的优缺点:
优点:
1. 支持随机访问:顺序表支持通过索引直接访问任意位置的元素,访问效率高。2. 紧凑的存储方式:元素在内存中是连续存储的,可以利用局部性原理提高访问效率。
3. 适合小规模数据:对于小规模数据集合,顺序表的管理可能更为简单和高效。
缺点:
1. 插入和删除效率低:在顺序表中,插入和删除元素可能需要移动其他元素,特别是在中间插入或删除元素时会导致较大开销。2. 固定大小:顺序表需要预先分配一定大小的内存空间,当元素数量超过容量时,可能需要进行扩容操作。
3. 空间浪费:如果预分配的空间过大,可能会造成空间浪费。
1.3 单链表的结构体定义
// 定义单链表节点数据类型为整型
typedef int SLTDateType;
// 定义单链表节点结构体
typedef struct SListNode
{
SLTDateType data; // 节点数据
struct SListNode* next; // 指向下一个节点的指针
} SListNode;
二、单链表的接口实现
2.1.初始化
SListNode* initSList(SListNode** head)
{
// 创建头节点
*head = (SListNode*)malloc(sizeof(SListNode));
if (*head == NULL)
{
printf("内存分配失败\n");
return NULL;
}
(*head)->next = NULL; // 头节点指针指向空
return *head;
}
2.2 单链表的插入
2.2.1 头插
// 头插法插入节点
void insertAtBeginning(SListNode** head, int data)
{
// 创建一个新节点
SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
// 检查内存分配是否成功
if (newNode == NULL)
{
printf("内存分配失败\n");
return;
}
// 设置新节点的数据为传入的数据
newNode->data = data;
// 将新节点的指针指向当前头节点
newNode->next = *head;
// 更新头节点指针,使其指向新节点,新节点成为新的头节点
*head = newNode;
}
2.2.2 尾插
// 尾插法插入节点
void insertAtEnd(SListNode** head, int data)
{
// 创建一个新节点
SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
if (newNode == NULL)
{
printf("内存分配失败\n");
return;
}
// 设置新节点的数据和指针域
newNode->data = data;
newNode->next = NULL;
// 如果链表为空,将头指针指向新节点
if (*head == NULL)
{
*head = newNode;
}
else
{
// 否则找到链表中最后一个节点
SListNode* current = *head;
while (current->next != NULL)
{
current = current->next;
}
// 将最后一个节点的指针指向新节点
current->next = newNode;
}
}
2.2.3 在pos位置后插入
// 在指定位置后插入节点
void insertAfterPos(ListNode* pos, int data)
{
// 检查传入的位置节点是否有效
if (pos == NULL)
{
printf("无效位置\n"); // 输出错误消息
return; // 返回
}
// 分配新节点的内存空间
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
if (newNode == NULL)
{
printf("内存分配失败\n"); // 输出错误消息
return; // 返回
}
// 设置新节点的数据域为传入的数据
newNode->data = data;
// 将新节点的指针域指向原位置节点的下一个节点
newNode->next = pos->next;
// 将原位置节点的指针域指向新节点,完成插入操作
pos->next = newNode;
}
2.3 单链表的删除
2.2.1 头删
// 头删
void deleteHead(ListNode** head)
{
// 检查头指针是否有效
if (*head == NULL)
{
printf("链表为空,无法删除\n"); // 输出错误消息
return; // 返回
}
// 保存头节点的地址以便释放内存
ListNode* temp = *head;
// 将头指针指向下一个节点
*head = (*head)->next;
// 释放原头节点的内存空间
free(temp);
printf("头节点已删除\n");
}
2.2.2 尾删
// 尾删
void deleteTail(ListNode** head)
{
// 检查头指针是否有效
if (*head == NULL)
{
printf("链表为空,无法删除\n"); // 输出错误消息
return; // 返回
}
// 如果链表只有一个节点
if ((*head)->next == NULL)
{
free(*head); // 释放头节点内存
*head = NULL; // 头指针置为NULL,表示链表为空
printf("尾节点已删除\n");
return;
}
// 找到倒数第二个节点
ListNode* temp = *head;
while (temp->next->next != NULL)
{
temp = temp->next;
}
// 保存尾节点的地址以便释放内存
ListNode* tail = temp->next;
// 尾节点的前一个节点指向NULL
temp->next = NULL;
// 释放原尾节点的内存空间
free(tail);
printf("尾节点已删除\n");
}
2.2.3 删除pos位置之后的值
// 删除pos位置之后的节点
void SListDeleteAfter(SListNode* pos)
{
if (pos == NULL || pos->next == NULL)
{
// 如果pos为空或者pos之后没有节点,则不进行删除操作
return;
}
SListNode* temp = pos->next; // 保存pos之后的节点
pos->next = temp->next; // 将pos的next指针指向pos之后节点的next节点,从而删除pos之后的节点
free(temp); // 释放已删除节点的内存
}
2.4 单链表修改指定位置节点值
// 修改指定位置节点的值
void SListModifyNodeValue(SListNode* pos, SLTDateType new_value)
{
// 检查节点是否存在
if (pos == NULL)
{
printf("无效的节点位置\n");
return;
}
// 修改节点的值为新值
pos->data = new_value;
printf("成功修改节点值\n");
}
2.5 单链表中查找特定数值的节点
// 在单链表中查找特定数值的节点
SListNode* SListFind(SListNode* head, SLTDateType value)
{
// 从头节点开始遍历链表
SListNode* current = head;
// 遍历链表直到找到目标值或到达链表末尾
while (current != NULL)
{
if (current->data == value)
{
// 找到目标值,返回该节点
return current;
}
current = current->next;
}
// 没有找到目标值,返回NULL
return NULL;
}
2.6 单链表动态申请节点
// 创建一个新的单链表节点并初始化数据为给定值
SListNode* BuySListNode(SLTDateType x)
{
// 分配内存空间用于新节点
SListNode* new_node = (SListNode*)malloc(sizeof(SListNode));
if (new_node == NULL)
{
// 内存分配失败
printf("内存分配失败\n");
return NULL;
}
// 初始化节点数据为给定值
new_node->data = x;
new_node->next = NULL; // 新节点的下一个节点指针设为NULL
return new_node;
}