【数据结构】单链表&&静态链表详解和代码实例

本文详细介绍了单链表和静态链表的概念,包括它们的存储结构、读取、插入和删除操作。单链表是一种动态分配存储单元的链式结构,而静态链表则利用数组元素的两个域实现。文中还提供了C++实现的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号【程序猿声】

01 单链表(Singly Linked List )

1.1 什么是单链表?

单链表是一种链式存储的结构。它动态的为节点分配存储单元。当有节点插入时,系统动态的为结点分配空间。在结点删除时,应该及时释放相应的存储单元,以防止内存泄露。由于是链式存储,所以操作单链表时,必须知道头结点或者头指针的位置。并且,在查找第i个节点时,必须找到第i-1个节点。

1.2 单链表的存储结构代码描述

对于链式存储,通过上一节的讲解相信大家已经了解得够清楚了。如下图所示:

下面我们来看看单链表存储是如何用代码来实现的。

1//单链表的存储结构C语言代码
2typedef struct SListNode
3{

4    datatype data;    //数据域
5    struct SListNode * pnext;//指针域
6}SLinkList;

由上面的结构我们可以看出,一个节点由存放数据的数据域和存放地址的指针域组成。假如p指向了第i个节点,那么p->data就是该节点存放的数据,而p->pnext自然就是指向下一个节点的指针。如下图所示:

那么接下来我们看看单链表的各个操作具体实现吧。(只讲几个关键步骤)
备注:下面的代码基于这样的一个单链表:

  • 有一个头指针phead
  • 有一个头结点node
  • 头指针指向头结点,头结点位置记为0
1.3 单链表的读取

在拿到头指针以后,单链表的读取也并非一件难事。一开始设置一个计数变量,不断遍历链表,让计数器自增。找到合适的位置将数据读取出来。具体代码实现如下:

 1#define status bool
2#define ERROR false
3#define OK true
4/*
5 * 函数功能:获取位置index节点的数据
6 * 参数说明:phead链表头结点,e用来获取的变量,index索引
7*/

8
9status GetSListIndexNode(Node * phead,DType *e, int index)
10
{
11    int icount = 0//计数器
12    //注:0号位为头结点,头结点不存放任何数据
13    if (phead->pnext == nullptr || index < 1 || index > GetSListLength()/*此处为链表长度*/)
14    {
15        return ERROR; //异常 处理
16    }
17    while (phead->pnext != nullptr)
18    {
19        icount++;
20        phead = phead->pnext;
21        if (icount == index)
22        {
23            *e = phead->data;
24            return OK;
25        }
26    }
27    return ERROR;
28}
1.4 单链表的插入
1.4.1 指定位置后插

其实链表的插入和删除都是很简单的操作,初学者只要抓住指针指向的节点,并加以区分开来,就很easy了。如下图:

图中,假如此时p指向了我们要插入的节点的位置。那么,怎样把我们的S节点给插入到p指向的节点之后?在这里我们先不要惊动p以及p后面的节点:

  1. 我们先让S节点指向p之后的节点(步骤①)
  2. 之后我们切断p和p后面那个节点的关系(步骤②)
  3. 最后让p节点的指针域指向s节点(步骤③),搞定

算法描述:

  1. 声明一个指针p指向链表头结点,向后遍历p=p->next,找到正确的位置。
  2. 新建一个结点s。
  3. s->next = p->next ①
  4. p->next = s ②③
    具体代码如下:
 1#define status bool
2#define ERROR false
3#define OK true
4/*
5 * 函数功能:指定位置后插
6 * 参数说明:phead链表头结点,IData插入的数据,index索引
7*/

8status InsertSListNodeFront(Node * phead, DType IData, int index)
9
{
10    if (phead->pnext == nullptr || index < 1 || index > GetSListLength())
11    {
12        return ERROR; //异常 处理
13    }
14    int iCount = 0//计数器
15    Node<DType> * q = nullptr//备用
16    while (phead->pnext != nullptr)
17    {
18        iCount++;
19        q = phead;
20        phead = phead->pnext;
21        if ( iCount == index )
22        {
23            Node<DType> * p = new Node<DType>;
24            p->data = IData;
25            p->pnext = phead;
26            q->pnext = p;   //前插
27            return OK;
28        }
29    }
30    return ERROR;
31}
1.4.2 指定位置前插

咳咳,聪明的小伙伴,用脑子想想。指定位置前插 == 指定位置的前一个位置进行后插。懂了吧?直接看具体代码:

 1/*
2 * 函数功能:指定位置后插
3 * 参数说明:phead链表头结点,IData插入的数据,index索引
4*/

5status InsertSListNodeBack(Node * phead, DType IData, int index)
6
{
7    if (phead->pnext == nullptr || index < 1 || index > GetSListLength())
8    {
9        return ERROR; //异常 处理
10    }
11    int iCount = 0//计数器
12    Node<DType> * q = nullptr//备用
13    while (phead->pnext != nullptr)
14    {
15        iCount++;
16        q = phead;
17        phead = phead->pnext;
18        if (iCount == index)
19        {
20            Node<DType> * p = new Node<DType>;
21            q = phead;
22            phead = phead->pnext; //后插就是后一个节点的前插咯
23            p->data = IData;
24            p->pnext = phead;
25            q->pnext = p;   
26            return OK;
27        }
28    }
29    return ERROR;
30}
1.5 单链表的删除

单链表的删除其实也是很简单。只要比如要删除p指向的节点,只需要让p之前的节点的指针域直接指向p之后的节点,再把p给free就OK了。如下图:

算法描述:

  1. 声明一个指针p指向链表头结点,向后遍历p=p->next,找到要删除的节点位置。
  2. q = p->next
  3. p->next = q->next ①②
  4. free(q) ③④

具体代码如下:

 1/*
2 * 函数功能:指定位置后插
3 * 参数说明:phead链表头结点,IData获取删除的数据,index索引
4*/

5//删除指定位置节点(e获取删除元素)
6template <typename DType>
7status DeleteSListIndexNode(Node * phead, DType *e, int index)
8
{
9    int i = 0//计数器
10    Node<DType> * q = nullptr;
11    if (phead->pnext == nullptr || index < 1 || index > GetSListLength())
12    {
13        return ERROR; //异常 处理
14    }
15    while (phead->pnext != nullptr)
16    {
17        i++;
18        q = phead; //保存备用
19        phead = phead->pnext;
20        if (i == index)
21        {
22            *e = phead->data;
23            q->pnext = phead->pnext; //删除出局
24            return OK;
25        }
26    }
27    return ERROR;
28}

代码应该不难,相信大家都能很容易看懂。

1.6.1 单链表的完整代码

好了,前面介绍了几个重要的操作,接下来请大家看看完整的代码吧。小编为了使用方便,就用C++的class和template将整个链表封装到了一个类里面,通过模板实现泛型编程。

  1/*
2 * 文件名:SingleLinkList.h
3 * 说明 :类的各种声明
4 */

5#pragma once //VC编译器防止头文件被重复包含的一条预编译指令
6
7#define status bool
8#define OK true
9#define ERROR false
10#define YES true
11#define NO false
12
13template <typename DType>
14class Node
15{

16public:
17    DType data;
18    Node * pnext;
19};
20
21template <typename DType>
22class CSingleLinkList
23{

24private:
25    Node<DType> *phead; //链表头指针
26public:
27    CSingleLinkList();//构造,类被创建时调用
28    ~CSingleLinkList();//析构,类被销毁时调用
29public:
30    //初始化链表
31    status InitSList();
32    //获取链表长度
33    int GetSListLength();
34    //增加一个节点 前插法
35    status AddSListNodeFront(DType idata);
36    //增加一个节点 后插法
37    status AddSListNodeBack( DType idata);
38    //判断链表是否为空
39    status IsSListEmpty();
40    //获取指定位置节点值(注意,本程序规定0号为头节点,e获取删除元素)
41    status GetSListIndexNode(DType *e, int index);
42    //删除指定位置节点(e获取删除元素)
43    status 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值