数据结构:单链表

目录

一、数据结构之单链表​编辑

1.1 单链表的定义

1.2 单链表与顺序表的优缺点对比

1.2.1 单链表的优缺点:

1.2.2 顺序表的优缺点:

1.3 单链表的结构体定义 

二、单链表的接口实现

2.1.初始化

2.2 单链表的插入

2.2.1 头插

2.2.2 尾插

2.2.3 在pos位置后插入

2.3 单链表的删除

2.2.1 头删

2.2.2 尾删

2.2.3 删除pos位置之后的值

2.4 单链表修改指定位置节点值

2.5 单链表中查找特定数值的节点

2.6 单链表动态申请节点


一、数据结构之单链表

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;
}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值