单链表分析

简介: 单链表分析

1. 什么是单链表

通过指针串联起来的内存空间,其内是结构体类型,包含存入数值与下一节点的地址,NULL是链表结束的标志(倘若结构体内存入的地址地址是NULL,则表示这是链表最后一个元素)

  1. 创造一个结构体类型
typedef struct SlistNode
{
    SLTDATATYPE data;
    struct SlistNode* next;
}SlistNode;//为使用方便起见,对结构体类型进行重命名
//我们要把SlistNode类型的结构体,以单链表的存储方式,存入内存空间

2.头插

通常情况(大多数人能想到的情况)链表内以及存在多个元素,我们要在第一个元素之前插入一个新元素

我们知道,链表是依靠指针串联起来的,但第一个指针是没有元素的指针可以指向的,所以我们创建一个

SlistNode* plist,让他指向第一个元素

在2元素前插入一个新元素1,我们需要让1的next指向2,plist指向1,因此代码如下

        SlistNode* newnode = (SlistNode*)malloc(sizeof(SlistNode));
        if (newnode == NULL)
        {
            perror("");
            return;
        }
        newnode->next = *pphead;//pphead接收的是plist的地址
        newnode->data = x;
        *pphead = newnode;

如果空间内没有元素呢?上述代码是否还适用?答案是否定的。因为空间内没有元素,插入的元素为第一个元素,所以该元素的next应该置为空,不指向任何空间。plist应该指向该元素。因而代码如下。

if (*pphead == NULL)//表示plist尚未指向任何空间,即链表为空
    {
        SlistNode* newnode = (SlistNode*)malloc(sizeof(SlistNode));
        if (newnode == NULL)
        {
            perror("");
            return;
        }
        newnode->next = NULL;
        newnode->data = x;
        *pphead = newnode;
    }

上述两种情况能否串联起来呢?

当链表为空时,插入第一个元素,将data置为x,next置为NULL,plist指向这块空间

现在我们要插入第二个元素,让2的next存放1的地址,即将next置为plist的值,然后让plist指向2的空间

因此上述两种情况的代码是可以衔接的,最终代码如下

void PushFront(SlistNode** pphead,SLTDATATYPE x)
{
    if (*pphead == NULL)
    {
        SlistNode* newnode = (SlistNode*)malloc(sizeof(SlistNode));
        if (newnode == NULL)
        {
            perror("");
            return;
        }
        newnode->next = NULL;
        newnode->data = x;
        *pphead = newnode;
    }
    else
    {
        SlistNode* newnode = (SlistNode*)malloc(sizeof(SlistNode));
        if (newnode == NULL)
        {
            perror("");
            return;
        }
        newnode->next = *pphead;
        newnode->data = x;
        *pphead = newnode;
    }
}

需要注意的是,由于我们创造了一个函数来实现功能,要改变plist的值,必须通过传地址的方式。

SlistNode* plist=NULL;
PushFront(plist,1)
void PushFront(SlistNode* pphead,SLTDATATYPE x)
{
pphead=0x112233;//这并不能改变plist存储的值,因为pphead仅仅是plist内存储的值的一份临时拷贝
}
 
SlistNode* plist=NULL;
PushFront(&plist,1)
void PushFront(SlistNode** pphead,SLTDATATYPE x)
{
*pphead=0x112233;//这可以改变plist的值,因为pphead接受的是plist的地址,pphead可以通过解引用改变值
//*pphead和plist是等价的
}

3. 尾插

假设空间内已有多个元素

尾插的第一步就是找尾,我们知道链表结束的标志是NULL。因此找到了NULL就找到了尾,其后我们要在尾后插入一个新元素,因此我们需要将原尾的next置为新元素的地址,新元素的next置为NULL,代码如下

SlistNode* newnode = (SlistNode*)malloc(sizeof(SlistNode));
        if (newnode == NULL)
        {
            perror("");
            return;
        }
        newnode->next = NULL;
        newnode->data = x;
        SlistNode* tail = *pphead;
        while (tail->next != NULL)
        {
            tail = tail->next;
        }
        tail->next = newnode;

现在来考虑空间内没有元素的情况,因为空间内没有元素,因此插入的元素是第一个,我们需要把它的next置为NULL,使plist指向它,代码如下

if (*pphead == NULL)
    {
        SlistNode* newnode = (SlistNode*)malloc(sizeof(SlistNode));
        if (newnode == NULL)
        {
            perror("");
            return;
        }
        newnode->next = NULL;
        newnode->data = x;
        *pphead = newnode;

上述两种情况的代码也是能够相链接的,最终代码如下

void PushBack(SlistNode** pphead,SLTDATATYPE x)
{
 
    if (*pphead == NULL)
    {
        SlistNode* newnode = (SlistNode*)malloc(sizeof(SlistNode));
        if (newnode == NULL)
        {
            perror("");
            return;
        }
        newnode->next = NULL;
        newnode->data = x;
        *pphead = newnode;
    }
    else
    {
        SlistNode* newnode = (SlistNode*)malloc(sizeof(SlistNode));
        if (newnode == NULL)
        {
            perror("");
            return;
        }
        newnode->next = NULL;
        newnode->data = x;
        SlistNode* tail = *pphead;
        while (tail->next != NULL)
        {
            tail = tail->next;
        }
        tail->next = newnode;

4. 头删

把第一个元素删除,让plist指向下一个元素。注意传地址修改

代码如下

void PopFront(SlistNode** pphead)
{
    assert(*pphead);//没有元素是不能删的
    {
        SlistNode* first = *pphead;
        *pphead = first->next;
        free(first);
        first == NULL;
    }
}

5. 尾删

假设空间有多个元素存在

尾删先找尾,将尾的空间释放掉,再将尾前一个元素的next置为NULL

        SlistNode* tail = *pphead;
        while (tail->next->next != NULL)//注意
        {
            tail = tail->next;
        }
        free(tail->next);
        tail->next = NULL;

如果只剩下一个元素了,上述代码显然就不适用了,因为while (tail->next->next != NULL)至少需要2个元素才能判断

因此当只剩下一个元素时,代码如下

    if (tail->next == NULL)
    {
        free(tail);
        tail == NULL;
        *pphead == NULL;
    }

完整的单链表头插头删尾插尾删链接

相关文章
|
7月前
|
存储 缓存 算法
链表全景:探索单链表、双向链表与循环链表【实战演练】
链表全景:探索单链表、双向链表与循环链表【实战演练】
87 3
|
6月前
|
存储
链表入门(单链表讲)
链表入门(单链表讲)
链表入门(单链表讲)
|
7月前
<数据结构>五道LeetCode链表题分析.环形链表,反转链表,合并链表,找中间节点.
<数据结构>五道LeetCode链表题分析.环形链表,反转链表,合并链表,找中间节点
62 1
|
6月前
|
存储 算法
数据结构和算法学习记录——线性表之单链表(上)-初始单链表及其尾插函数(顺序表缺陷、单链表优点、链表打印)
数据结构和算法学习记录——线性表之单链表(上)-初始单链表及其尾插函数(顺序表缺陷、单链表优点、链表打印)
45 0
|
6月前
|
存储 算法
数据结构和算法学习记录——线性表之顺序表(顺序表概念、结构、顺序表接口函数-头插头删、尾插尾删)
数据结构和算法学习记录——线性表之顺序表(顺序表概念、结构、顺序表接口函数-头插头删、尾插尾删)
32 0
单链表————单链表的构建,增删查改功能的实现
单链表————单链表的构建,增删查改功能的实现
|
存储 C语言
什么是链表,如何实现?(单链表篇)
什么是链表,如何实现?(单链表篇)
80 0
什么是链表,如何实现?(单链表篇)
|
存储 算法 Java
Java数据结构与算法分析(三)链表(单链表、双链表、环形链表)
通过前篇文章《[数组](https://blog.csdn.net/gozhuyinglong/article/details/109702860)》了解到数组的存储结构是一块连续的内存,插入和删除元素时其每个部分都有可能整体移动。为了避免这样的线性开销,我们需要保证数据可以不连续存储。本篇介绍另一种数据结构:链表。
227 0
|
存储
邻接表(单链表)详细分析,言简意赅
邻接表(单链表)详细分析,言简意赅
133 0