学习链表首先我们要知道什么是链表
1.概念
链表是一种常见的数据结构,它由一系列节点组成,每个节点包含一个值以及指向下一个节点的指针。在C语言中,链表通常用于实现动态内存分配和数据结构的动态创建和删除。 链表中的节点是动态分配的,这意味着链表的大小可以在运行时改变。链表的第一个节点称为头节点。链表的最后一个节点称为尾节点,它包含一个值为NULL的指针,表示链表的结束。 C语言中的链表可以使用结构体函数指针来实现。链表的操作包括创建、插入、删除节点以及遍历链表等。链表的创建和删除通常使用malloc和free函数来实现动态内存分配。
2.链表相比于顺序表的优势
链表相比数组的优势在于,链表不需要预先分配固定大小的内存空间,因此可以节省内存空间,并且链表的插入和删除操作只需要修改指针即可,不需要移动数据,因此效率更高。
3.链表相比于顺序表的劣势
-
随机访问效率低:链表是一种动态数据结构,其元素在内存中不是连续存储的,而是通过指针链接起来的。这导致在链表中进行随机访问元素时效率较低,需要遍历链表直到找到目标元素。
-
内存占用较大:链表中每个节点都需要额外的指针来指向下一个节点,这会导致链表相对于顺序表具有更高的内存开销。此外,由于链表的动态特性,还会产生额外的内存分配和释放操作。
-
插入和删除操作的效率:链表在插入和删除元素时可以在 O(1) 的时间复杂度内完成,只需调整指针的指向即可。然而,如果需要定位到插入或删除位置,则需要遍历链表,使得操作的效率受到链表长度的影响。
-
不支持随机访问和排序:链表不支持像顺序表那样的随机访问,因为需要从头开始遍历链表才能访问特定位置的元素。此外,链表结构的特性使得对链表进行排序操作相对困难,需要额外的算法和操作。
我们既然了解了什么是链表,那我们不妨来写一个吧。
写链表呢,我们就必须创建一个结构体出来
这里将int类型typedef是为了适用于其他类型
我们虽然有了结构体,但我们的链表还是空链表,我们不妨写一下它的头插和尾插来增加一些数据
申请节点的函数
为了验证我们的头插,尾插是否真确,我们不妨写个打印函数,将他们打印出来
我们应该如何调用这些函数呢,我们不能只写函数不去使用吧,聪明的你应该也跟我一样,知道在main函数中调用
很明显我们写的是真确的,很多细心的小伙伴可能会发现我们传的是结构指针的地址,所以我们在接受参数的时候必须用二级指针去接受,当然,小伙伴们也是可以用返回值来接收的。
很多小伙伴可能要好奇了,为什么要传指针的地址呢。
其实原因也很简单,我们这里创建的是没有哨兵位头结点的单链表,我们要去改变它的头结点,所以我们就应该传它的地址过去。
我们现在有了头插和尾插,那我们想删除数据应该怎么办呢,哈哈,那当然是进行头删和尾删喽。
我们既然有了头删和尾删,我们不妨也测试一下
我想聪明的你们应该也发现了一种极端情况,当我们的链表被删的没有数据了应该怎么办,是继续删吗,答案当然是否定的,在这里我使用了比较暴力的解法:断言(assert)报错,当然你们也是可以使用相对温柔的做法的。用if语句去判断
头插,尾插,头删,尾删,我们都用了。那我们想在任意位置插入应该怎么办呢
我们想任意位置删除数据也是同样的
既然我们有了任意位置的删除和增加,我们不妨也测试一下呗
很明显,也是没有问题的,那我们要是不想删除Pos,而是想删除Pos后面数据,应该怎么办呢
到这里,那么恭喜你,我们的链表基本完成了。但我们malloc申请出来的空间,会一直存在,那我们是不是应该去释放一下呢。
OK,今日目标已经完成。