前言
📚作者简介:爱编程的小马,正在学习C/C++,Linux及MySQL。
📚本文收录于初阶数据结构系列,本专栏主要是针对时间、空间复杂度,顺序表和链表、栈和队列、二叉树以及各类排序算法,持续更新!
📚相关专栏C++及Linux正在发展,敬请期待!
目录
1. 链表
1.1 链表的定义
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序所决定的
本文只介绍最简单的链表结构:单链表
1.2 链表与顺序表相比的好处
1、顺序表从中间插入/头部插入,时间复杂度是O(N),因为要一次往后挪动,但是单链表是O(1),大大节省了程序运行时间。
2、 顺序表每次需要增容,到后期增容很大的时候,需要拷贝数据、开辟新空间、释放旧空间。会有不小的损耗。链表直接开辟一个结构体大小的空间即可。
3、增容一般是两倍,但是我就想多插入几个仅此而已,会造成空间的大规模浪费。链表同样更加简单且占用空间小。
1.3 链表的结构表示
首先要给大家介绍一下,就是链表中的结点是一个结构体,结点中一个变量是存储数据的,另一个变量是存储结构体地址的,上一个结点存下一个结点的地址,从而链接起来。
1.3.1 链表的结构形式
1.3.2 链表的结构性质
1、从上图可以看出,链表在逻辑上是连续的,在物理地址上是不连续的
2、现实的结点是动态内存在堆区申请出来的
3、堆上申请的空间,是按照一定的规律来的,有些可能相同,有些可能不同。
1.4 单链表的实现
1.4.1 单链表的创建
上文我们提到了,结点是一个结构体,第一个结构体变量是存储数据的,第二个是存储下一个链表的地址的
typedef int SListDataType;
typedef struct SListNode
{
SListDataType data;
struct SListNode* next;
}SLTNode;
1.4.2 单链表的打印
void PrintSList(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL");
}
为什么是这样子打印的?给大家说一下思想:
1、单链表定义了最后一个链表的地址为空指针,所以我们就定义了一个cur来遍历整个链表
2、每找到一个数据我们就打印,然后遍历链表指针cur就往后走一步, 怎么走?是不是next中存放了下一个结点的地址,那么把cur管理的结构体中next的地址赋值给cur是不是相当于向后走了一步。
1.4.3 单链表的动态内存申请
SLTNode* BuySLTNode(SListDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
比方说,我想申请一块动态内存空间,里面存储x的值,那么这个时候,就通过malloc在堆上申请一块空间,交给newnode管理,这时候把newnode中data的值赋值为x,newnode中next的值赋值为NULL后返回这块空间的地址。这是不是就很好的开辟了一个结点。如果开辟失败了就返回空指针。