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

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后面的节点:
- 我们先让S节点指向p之后的节点(步骤①)
- 之后我们切断p和p后面那个节点的关系(步骤②)
- 最后让p节点的指针域指向s节点(步骤③),搞定
算法描述:
- 声明一个指针p指向链表头结点,向后遍历p=p->next,找到正确的位置。
- 新建一个结点s。
- s->next = p->next ①
- 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了。如下图:

算法描述:
- 声明一个指针p指向链表头结点,向后遍历p=p->next,找到要删除的节点位置。
- q = p->next
- p->next = q->next ①②
- 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