数据结构——单链表的实现

目录

一、链表的概念

二、链表的结构

三、链表的性质

四、链表的打印

五、单链表的实现

(1)SList.h

(2)SList.c

(3)提示

六、写在最后


一、链表的概念

链表是物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

二、链表的结构

(1)链表的结构类似于火车,火车是由一节节车厢和挂钩连接起来的。结合这个形象的图,其实链表是由一个个结点组成的,而结点与结点之间由指针连接起来。

(2)结点是独立申请下来的空间, 主要有两部分:当前节点保存的数据和下一个结点的地址。

三、链表的性质

(1)链式结构在逻辑上连续,在物理上不连续。因此,不能对链表的数据进行随机访问;

(2)结点一般是从堆上申请的,每次申请的空间可能连续,也可能不连续。

(3)每个结点对应的结构体(假设当前保存的结点为整型)

typedef int SLTDatatype;
typedef struct SListNode
{
    SLTDatatype data;//结点保存的数据
    struct SListNode* next;//指针变量用来保存下一个结点的地址
}SLTNode;

四、链表的打印

思路:创建变量pcur保存第一个结点的数据,使用结点与结点之间的联系来遍历所有结点,并将每个结点保存的数据打印出来。

void SLTPrint(SLTNode* phead)
{
    SLTNode* pcur = phead;//创建指针用来遍历所有结点
    while(pcur)
    {
        printf("%d->",pcur->data);//打印结点的数据
        pcur = pcur->next;//pcur指向其下一个结点
    }
    printf("\n");
}

五、单链表的实现

(1)SList.h

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int SLTDatatype;
typedef struct SListNode
{
    SLTDatatype data;
    struct SListNode* next;
}SLTNode;

//打印
void SLTPrint(SLTNode* phead);

//申请空间
SLTNode* SLTBuyNode(SLTDatatype x);

//头插
void SLTPushFront(SLTNode** pphead, SLTDatatype x);

//尾插
void SLTPushBack(SLTNode** pphead, SLTDatatype x);

//头删
void SLTPopFront(SLTNode** pphead);

//尾删
void SLTPopBack(SLTNode** pphead);

//查找数据的位置
SLTNode* SLTFind(SLTNode* phead, SLTDatatype x);

//在指定位置之前插入
void SLTInsertBefore(SLTNode** pphead, SLTNode* pos, SLTDatatype x);

//在指定位置之后插入
void SLTInsertAfter(SLTNode* pos, SLTDatatype x);

//删除指定位置的结点
void SLTErase(SLTnode** pphead, SLTNode* pos);

//删除指定位置之后的结点
void SLTEraseAfter(SLTNode* pos);

//销毁链表
void SLTDestroy(SLTNode** pphead);

(2)SList.c

#include "SList.h"

//打印
void SLTPrint(SLTNode* phead)
{
    SLTNode* pcur = phead;
    while(pcur)
    {
        printf("%d->",pcur->data);
        pcur = pcur->next;
    }
    printf("NULL\n");//遍历完成跳出循环,此时指向NULL
}

//申请空间
SLTNode* SLTBuyNode(SLTDatatype x)
{
    SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));//注意单位是字节
    if(node == NULL)//申请空间失败
    {
        perror("malloc fali!\n");
        exit(1);//退出
    }
    node->data = x;
    node->next = NULL;
    return node;
}

//头插
void SLTPushFront(SLTNode** pphead, SLTDatatype x)//需要改变头结点,所以传地址
{
    assert(pphead);
    SLTNode* newnode = SLTBuyNode(x);
    newnode->next = *pphead;
    *pphead = newnode;
}


//尾插
void SLTPushBack(SLTNode** pphead, SLTDatatype x)
{
    assert(pphead);
    SLTNode* newnode = SLTBuyNode(x);
    if(*pphead == NULL)
    {
        *pphead = newnode;
    }
    else
    {
        SLTNode* ptail = *pphead;
        while(ptail->next)
        {
            ptail = ptail->next;
        }
        ptail->next = newnode;
    }
}

//头删
void SLTPopFront(SLTNode** pphead)
{
    assert(pphead && *pphead);
    SLTNode* del = *pphead;
    *pphead = del->next;
    free(del);
    del = NULL;
}


//尾删
void SLTPopBack(SLTNode** pphead)
{
    assert(pphead && *pphead);
    if((*pphead)->next == NULL)//只有一个结点,要分开讨论:因为没有prev
    {
        free(*pphead);
        *pphead = NULL;
    }
    else
    {
        SLTNode* pcur = *pphead;
        SLTNode* prev = NULL;
        while(pcur->next)
        {
            prev = pcur;
            pcur = pcur->next;
        }
        free(pcur);
        pcur = NULL;
        prev->next = NULL;
    }
}

//查找数据的位置
SLTNode* SLTFind(SLTNode* phead, SLTDatatype x)//不需要改变头结点,因此可以传值
{
    assert(phead);
    SLTNode* pcur = phead;
    while(pcur)
    {
        if(pcur->data == x)
        {
            return pcur;
        }
        pcur = pcur->next;
    }
    return NULL;
}


//在指定位置之前插入
void SLTInsertBefore(SLTNode** pphead, SLTNode* pos, SLTDatatype x)
{
    assert(pphead);//*pphead可以为空
    assert(pos);
    if(*pphead == pos)
    {
        SLTPushFront(pphead,x);
    }
    else
    {
        SLTNode* newnode = SLTBuyNode(x);
        SLTNode* prev = *pphead;
        while(prev->next != pos)
        {
            prev = prev->next;
        }
        prev->next = newnode;
        newnode->next = 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 && *pphead);
    assert(pos);
    if(*pphead == pos)
    {
        SLTPopFront(pphead);
    }
    else
    {
        SLTNode* prev = *pphead;
        while(prev->next != pos)
        {
            prev = prev->next;
        }
        prev->next = pos->next;
        free(pos);
        pos = NULL;
    }
}

//删除指定位置之后的结点
void SLTEraseAfter(SLTNode* pos)
{
    assert(pos && pos->next);//pos之后也不能为空
    pos->next = (pos->next)->next;
    SLTNode* del = pos->next;
    free(del);
    del = NULL;
}

//销毁链表
void SLTDestroy(SLTNode** pphead)
{
    assert(pphead && *pphead);
    SLTNode* pcur = *pphead;
    while(pcur)
    {
        SLTNode* next = pcur->next;
        free(pcur);
        pcur = NULL;
    }
    *pphead = NULL;//最后将其置为空
}

(3)提示

与顺序表一样,每写完一个方法可以在test.c中测试一下,以免因为代码过多过长而不容易排查出错原因~ 

六、写在最后

关于单链表,后续还会有算法题,敬请期待~~

  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值