【C语言】基本数据结构-单链表

目录

 

一. 背景介绍

1. 链表简介

2. 链表的优势

3. 链表支持的操作

二. 单链表应用实例

1. 单链表初始化

2. 单链表判空

3. 头部插入数据

4. 尾部插入数据

5. 查询指定数据

6. 删除指定数据

7. 打印链表数据

 


 

一. 背景介绍

1. 链表简介

        链表作为一种常用的数据结构,在工作中会被广泛的使用,因此链表的原理和使用方法是每一个C语言开发工程师必不可少的学习要点。

        链表是一种线性的数据结构,由多个结构相同的节点相连接而成,每一个节点都是一个结构体变量,其中包含了数据域和指针域,数据域用于存储想要保存的数据,而指针域存储一个指向下一个节点的指针。

        根据结构区分,链表又分为单链表,双链表和循环链表,本章节只讲解单链表如何应用。其余两种链表的应用在后续章节中讲解。

2. 链表的优势

        下表列出了链表和数组的优缺点。通过对比我们会发现,链表适用于插入和删除元素比较频繁的场景。

 存储方式占用开销初始化查询节点速度插入和删除节点速度
链表不连续包括指针域,因此占用开销大。初始化时不需要分配固定空间。慢,查询特定节点需要从头节点依次遍历。快,插入和删除只需要移动前后两个节点。
数组连续只包含数据域,因此占用开销小。初始化时需要把分配固定空间。快,可以直接通过下标索引值进行查询。慢,插入和删除需要移动当前索引后面的所有元素。

3. 链表支持操作

        链表支持的基本操作如下表所示。

/*单链表支持的基本操作*/

//初始化链表
void init_list(struct node **pNode);

//链表判空
bool list_is_empty(struct node *pHead);

//头部插入数据
void insert_Firstnode(struct node *pHead, int element);

//尾部插入数据
void insert_Lastnode(struct node *pHead, int element);

//查询指定数据
struct node* find_element(struct node *pHead, int element);

//删除指定数据
void delete_last_element(struct node* pHead, int element);

//打印链表
void print_list(struct node *pHead);

 

二. 单链表应用实例

1. 单链表初始化

        下面进行了链表中头节点的初始化,个人建议头结点不要用来存储数据,头节点的作用只是为了固定链表的初始地址,因此通常要从头结点的后一个结点开始存储数据。

/*声明链表节点*/
struct node 
{
    int element;       //数据域
    struct node *next; //指针域
}

/*链表初始化函数*/
void init_list(struct node **pNode)
{
    *pNode = (struct node*)malloc(sizeof(struct node));
    if(NULL == *pNode)
    {
        printf("链表初始化申请内存失败");
        return NULL;
    }
    *pNode ->next = NULL;
}

/*测试函数*/
void test_code(void)
{
    struct node *ptr = NULL;
    init_list(&ptr);
    ....
}

2. 单链表判空

        单链表的判空实际上就是对头结点指针域的判空,只要头结点的指针域为NULL,就说明链表是空链表。

(1)这里需要特别注意链表判空的条件是头节点的指针域pHead->next为NULL,而并非头节点地址pHead为NULL,头节点地址为NULL实际上代表头节点不存在,也就是链表不存在。

/*判断链表是否为空的函数*/
bool list_is_empty(struct node *pHead)
{
    //链表是否存在
    assert(NULL != pHead);

    if(NULL == pHead->next)
    {
        return FALSE;
    }

    return TRUE;
}


/*测试函数*/
void test_code(void)
{
    //传入链表的头结点
    list_is_empty(ptr);
    ....
}

3. 头部插入数据

        头部插入新节点,实际上就是在头节点之后插入新节点,需要考虑以下几种情况。

(1)插入前必须保证链表存在。

(2)链表中只有头节点时,我们只需要创建新节点,然后将头节点和新节点连接即可。

(3)链表中除了头节点之外还有其他节点时,我们需要创建新节点,将头节点的指针域指向新节点,然后将新节点的指针域指向原来头节点的下一个节点即可。

/*函数功能:插入新节点函数*/
/*函数参数:head-链表的头节点  element-要插入的元素值*/
void insert_Firstnode(struct node *pHead, int element)
{
    //链表是否存在
    assert(NULL != pHead);

    //避免直接使用参数指针
    struct node *pNode = pHead;

    //初始化要插入的节点
    struct node *pNew = NULL;
    pNew =(struct node *)malloc(sizeof(struct node));
    if(NULL == pNew)
    {
        printf("创建插入节点失败");
        return NULL;
    }
    pNew->element = element;

    //插入数据
    if(NULL == pNode)   //原来的链表中只有头节点
    {
        pNode->next = pNew;
        pNew->next = NULL;
    }
    else     //原来的链表中除了头结点外还有其他节点
    {
        pNode->next = pNew;
        pNew->next = pNode->next;
    }
}

/*测试函数*/
void test_code(void)
{   
    //往链表ptr中添加元素element
    sinsert_Firstnode(ptr, element);
    .....
}

4. 尾部插入数据

        尾部插入数据的方法是将指针指向尾节点,并在最后进行插入,并将插入的节点作为尾节点。

/*函数功能:插入新节点函数*/
/*函数参数:head-链表的头节点  element-要插入的元素值*/
void insert_Lastnode(struct node *pHead, int element)
{
    //链表是否存在
    assert(NULL != pHead);

    //避免直接使用参数指针
    struct node *pNode = pHead;

    //初始化要插入的节点
    struct node *pNew = NULL;
    pNew =(struct node *)malloc(sizeof(struct node));
    if(NULL == pNew)
    {
        printf("创建插入节点失败");
        return NULL;
    }
    pNew->element = element;

    //遍历链表直到指向最后一个节点
    while(NULL == pNode->next)
    {
        pNode = pNode->next;
    }
    
    //将尾节点和插入节点连接
    pNode->next = pNew;
    pNew->next = NULL;
}

/*测试函数*/
void test_code(void)
{   
    //往链表ptr中添加元素element
    insert_Lastnode(ptr, element);
    .....
}

5. 查询指定数据

        查询指定数据的思路也很简单,直接从头结点开始遍历节点,直到查到数据为止。

/*查询链表函数*/
struct node* find_element(struct node *pHead, int element) 
{
    //链表是否存在
    assert(NULL != pHead);

    //避免直接使用指针
    struct node *pNode = pHead;

    //遍历链表
    while((NULL != pNode) && (element != pNode->element))
    {
        pNode = pNode->next;
    }
    
    if(element == pNode->element)
    {
        returm pNode;
    }
    else
    {
        printf("链表中查找不到元素");
        return NULL;
    }
}

/*测试函数*/
void test_code(void)
{
    //在pNode中查询数据element,并且返回对应的节点地址
    pNode = find_element(pNode, element);
    ....
}

6. 删除指定数据

        删除指定的数据可以拆分成两个功能,先查找指定数据的上一个节点,然后将对应的数据删除即可。需要注意的点有

(1)当链表为空时,直接返回NULL即可。

(2)当删除的链表节点为中间节点时,需要把当前数据所在节点的前一个节点指向后一个节点,并且将当前节点内存释放。

(3)当删除的链表节点为最后一个节点时,只需要将当前数据所在节点的前一个节点指向NULL,然后释放当前节点内存即可。

/*删除链表中的指定元素*/
void delete_last_element(struct node* pHead, int element)
{
    //链表是否存在
    assert(NULL != pHead);

    //避免直接使用指针
    struct node* pNode = pHead;
    
    //查找当前数据的前一个节点p
    while((NULL != pNode->next) && (element != pNode->next->element))
    {
        pNode == pNode->next;
    }
    
    //删除目标节点p->next
    if(NULL == pNode->next->next)   //目标节点是链表的最后一个节点
    {
        pNode = NULL;
        free(pNode->next);
    }   
    else      //目标节点不是链表的最后一个节点
    {
        pNode = pNode->next->next;
        free(pNode->next);
    }
}

7. 打印链表数据

        打印链表数据的操作非常的简单,就是遍历链表然后将数据打印即可。

/*打印链表数据*/
void print_list(struct node *pHead)
{
    //链表是否存在
    assert(NULL != pHead);

    //避免直接使用指针
    struct node *pNode = pHead;
    while(NULL != pNode)
    {
        print("address %p value is %d", pNode, pNode->element);
    }
    return ;
}

 

 

 

 

  • 23
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

电脑玩家饮水机

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值