数据结构:单链表

单链表的全称为"不带头单向不循环链表"

注意:

        下文提到的头节点与带头链表时两个概念,文中的头节点指的是第一个有效节点,二带头节点中的头指的是第一个无效节点

链表和顺序表一样,都是线性表,逻辑结构上是线性的,但不同的是,链表在物理结构上不是线性的

链表是由一个一个节点构成的,一个节点分为两部分:存储的数据和指针(结构体指针)

其中的指针存储的是该节点指向的下一个节点的地址

一个节点的结构体可以这样表示:

typedef int SLDataType;
typedef struct SListNode
{
    int date;
    struct SListNode* next;
}SLTNode;

一.链表的打印

SLT = single linked list

void SLTPrint(SLNode * phead)
{
    SLTNode* pcur = phead;
    while(pcur != NULL)//遍历链表,pcur为空时跳出循环
    {
        printf("%d->",pcur->data);
        pcur = pcur->next;
    }
    printf("NULL\n");
}

二.链表的尾插

void SLTPushBack(SLTNode** pphead,SLTDataType x)
{
    if(pphead == NULL)//如果为空,则根本不存在链表,自然不能插入
        return;
    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    newnode->data = x;
    newnode->next = NULL;

    //链表存在,但是内容为空,新节点作为phead
    if(*pphead == NULL)
        {
            *pphead = newnode;
            return;
        }
    //链表不为空
    SLTNode* ptail = *pphead;
    while(ptail->next != NULL)//找到链表的尾节点,pcur->next为空时,则当前pcur为链表的最后一个节点
        ptail = ptail->next;

     ptail->next = newnode;
}

注意:需要改变的是指针的值,因此在传参时需要传入指针的地址,,因此函数需要一个二级指针作为形参

三.链表的头插

//申请一个新节点
SLTNode* SLTBuyNode(SLTDataType x)
{
    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    newnode->data = x;
    newnode->next = NULL;

    return newnode;
}
void SLTPushFront(SLTNode** pphead,SLTDataType x)
{
    if(pphead == NULL)//如果为空,则根本不存在链表,自然不能插入
        return;
    SLTNode* newnode = SLTBuyNode(x);
    
    newnode->next = *pphead;
    *pphead = newnode;
}

四.链表的尾删

void SLTPopBack(SLTNode** pphead)

{
    if(pphead == NULL)
        return;
    
    if(*pphead == NULL)
        return;
    
    //链表只有一个节点
    if((*pphead)->next == NULL)
        {
        free(*pphead);
        *pphead = NULL;
        return;
        }

    //链表有多个节点
    SLTNode* ptail = *pphead;//尾节点
    SLTNode* prev = NULL;//尾节点的前一个节点
    while(ptail->next != NULL)
    {
        prev = ptail;
        ptail = ptail->next;
    }
    prev->next = NULL;
    free(ptail);
    ptail = NULL;
}

五.链表的头删

void SLTPopFront(SLTNode** pphead)
{
    assert(pphead);
    assert(*pphead);

    SLTNode* next = (*pphead)->next;
    free(*pphead);
    *pphead = next;
}

六.链表数据的查找

void SLTFind(SLTNode* phead,SLTDataType x)
{
    assert(pphead);
    SLTNode* pcur = phead;
    while(pcur != NULL)
    {
        if(pcur->data == x)
            return pcur;
        pcur = pcur->next;
    }
    //没有找到
    return NULL;
}

七. 指定位置前插入数据

void SLTInsert(SLTNode** pphead,SLTNode* pos,SLTDataType x)
{
    assert(pphead);
    assert(pos);
    //链表不能为空,如果为空,则pos数据一定不合法
    assert(*pphead);
    
    //pos是头节点时
    if(pos == *pphead)
    //头插
    {
    SLTPushFront(pphead,x);
    return;
    }

    //pos不是头节点时
    //pos如果时头节点,则while死循环,程序报错
    SLTNode* prev = *pphead;
    while(prev->next != pos)//prev最终为pos前的节点
    {
        prev = prev->next;
    }
    //prev->newnode->pos
    prev->next = newnode;
    newnode->next = pos;
}

八.指定位置后插入数据

//由于该链表为单向链表,所以在找pos前面的节点时就需要遍历链表
//但是在找pos后的节点时,就可以直接用pos定位,不需要头节点了
void SLTInsertAfter(SLTNode* pos,SLTDataType x)
{
    assert(pos);

    SLTNode* newnode = SLTBuyNode(x);
    newnode->next = pos->next;
    pos->next = newnode;
}

九.删除特定节点

void SLTErase(SLTNode** pphead,SLTNode* pos)
{
    assert(pphead);
    assert(pos);
    assert(*pphead);

    //删除的节点是头节点时    
    if(*pphead == pos)
    {
        SLTPopFront(pphead);
        return;
    }
    //删除的节点不为头节点时
    SLTNode* prev = *pphead;
    //找到pos前的节点
    while(prev->next != pos)
    {
        prev = prev->next
    }

    prev->next = pos->next;
    free(pos);
    pos = NULL;
}

十.删除特定节点之后的节点

void SLTEraseAfter(SLTNode* pos)
{
    assert(pos);
    assert(pos->next);

    //先保存需要删除的节点
    SLTNode* del = pos->next;
    pos->next = pos->next->next;
    free(del);
    del = NULL;
}

十一.销毁链表

void SListDestory(SLTNode* pphead)
{
    //由于链表的存储不是线性的,所以无法一次销毁整个链表
    //因此选择循环删除链表(一个一个删)
    SLTNode* pcur = *pphead;
    while(pcur != NULL)
    {
        SLTNode* next = pcur->next;
        free(pcur);
        pcur = next;
    }
    *pphead = NULL;
}

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值