C语言实现单向链表

该文章已生成可运行项目,


前言

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,效率比较高,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,效率一般。本章我们就使用C语言来实现一个单向链表。


一、链表的分类

在实现链表之前,我们先讲解一下链表的分类。首先链表分为带哨兵位的头结点的链表和不带哨兵位的头结点的链表。这里说明一下,在链表中使用哨兵位的头结点的做法主要是为了简化链表的操作,尤其是在处理空链表和边界条件时。哨兵位的头结点本身并不存储数据,而是作为一个标志,帮助我们在执行插入、删除等操作时,不必额外检查是否为空链表或者其他特殊情况。其次链表可以根据节点之间的连接方式可以分为单向链表,双向链表。最后还可以根据尾结点是否和头结点相连看是否为循环链表
我们将这几种分类列出来如下:
带头 不带头
单向 双向
循环 不循环

任意组合即可组成常见的8种链表分类。不过这里需要说明一下,虽然可以排列组合出这么多种分类的链表,但最常用的其实只有两种–不带头单向不循环链表和带头双向循环链表,而我们平常所说的单向链表和双向链表一般也就分别特指这两种类型的链表。本章我们要实现的单向链表即是不带头单向不循环链表。

二、单向链表结构

//单向链表
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

typedef int SLTDataType;

typedef struct SListNode
{
   
   
    SLTDataType data;
    struct SListNode* next;
}SLTNode;

在C语言中我们使用一个结构体来代表链表的每一个结点。这里我们定义了一个SListNode的结构体来代表链表的结点并将其重命名为SLTNode,在这个结构体中我们定义一个存储数据的变量data,再定义一个指向下一个结点的指针next。在这里需要注意两点,第一,在这里和上一章的顺序表类似,我们将int重命名为SLTDataType,这样便于以后我们更改链表存储数据的类型;第二,我们在定义指向下一个结点的指针时不能写成SLTNode* next,因为我们在定义该结点时还未进行重命名,所以在定义的结构体里面只能老老实实地写全struct SListNode* next


三、单向链表实现

1.打印

void SLTPrint(SLTNode* phead)
{
   
   
    assert(phead);
    while (phead)
    {
   
   
        printf("%d->", phead->data);
        phead = phead->next;
    }
    printf("NULL\n");
}

首先我们写一个打印链表的函数,这样便于我们后面进行测试的时候进行观察。打印函数即是利用while循环对链表进行遍历,然后依次进行打印操作,当结点为空即跳出循环完成遍历,也就完成了打印链表的操作。


2.尾插和尾删

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
   
   
    assert(pphead);
    SLTNode* new = (SLTNode*)malloc((sizeof(SLTNode)));
    if (new == NULL)
    {
   
   
        perror("malloc");
    }
    new->data = x;
    new->next = NULL;
    if (*pphead == NULL)
    {
   
   
        *pphead = new;
    }
    else
    {
   
   
        SLTNode* cur = *pphead;
        while (cur->next)
        {
   
   
            cur = cur->next;
        }
        cur->next = new;
    }   
}

尾插函数,首先我们和之前实现顺序表一样,在每个函数前面加上一句assert断言,避免传入空指针的情况。这里说明一下,我们避免传入的是空指针是避免传入的pphead二级指针是空指针,而pphead一级指针是可以为空的,因为我们实现的不带哨兵位头结点的链表,所以当链表为空时,pphead一级指针即为空指针。在尾插函数中,我们先使用malloc函数对新结点进行动态开辟,并将插入的数据赋值给新结点的data,然后将其指向下一个结点的指针赋值为空。紧接着我们就要进行分类讨论,即当前链表是否为空。当当前链表为空,即 pphead == NULL,我们就直接将新结点赋值给 *pphead。若当前链表不为空**,我们就对该链表进行遍历找到链表的尾结点,然后对其进行链接,即让尾结点的next指针指向新结点

//尾删
void SLTPopBack(SLTNode** pphead)
{
   
   
    assert(pphead && *pphead);
    if ((*pphead)->next == NULL)
    
本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值