线性结构的C实现之链表(面向新手,大佬勿喷)

链式存储的链表

对于线性结构还有一种实现模式,就是链表,相比于数组,链表的优势在于,他理论上没有最大的规模,不像数组需要规定一个明确的最大值。而且插入删除操作相比于数组来讲比较容易,但是实现起来比较花里胡哨,需要一段时间去训练,实现原理还是很简单的。

注意:链表的操作建议同学画图理解。

建立链表

我们是这样建立链表的,首先是建立一种叫做结构体的东西,这种东西呢将各种基本结构融合在一起,然后进行一个封装,这一点C做的相对没有那么强,封装只能选择假装已经封装了哈。。毕竟C面向过程。对于链表就需要这样一种封装,即制作一个节点结构体,这种结构体具有一个特点,就是像一个大头钉,头是橡皮做的,钉是纯铁工制造。我们的目的就是让这些大头钉一个钉子扎在橡皮里,橡皮上的钉子钉在下一个橡皮里,就这么循环往复地扎下去,最后一个没地扎,那就扎地里,也就是所谓的置空。

#include<stdio.h>
typedef struct Node
{
	int data;//以整型为例解释一哈。
	struct Node* next;//这就是链表的特点,具有一个后继指针操作。
}node, *pnode;//pnode就是指指向链表节点的指针pointer node
int main()
{
	pnode LinkList = (node*)malloc(sizeof(node));//链表都是要一个头节点的,当然你不要也行,实现起来会有很多麻烦
	LinkList->next = NULL;
	LinkList->data = 0;//初始化,这是我的习惯,如果操作够稳,这些操作实际用处不大
	return 0;
}

LinkList 就是代表一个链表,不过这个链表是空的,我们需要对其进行初始化。有两种初始化的形式,建议大家熟记运用。

首先是头插法,基本思想是,你不是有一个头节点*LinkList嘛,然后我每次添加进去的节点都放在最前面(最前面即是指LinkList->next的位置)。
具体操作和插入一个节点的思想是一致的,如果我想将ptr创建的节点插在某节点N的后面成为N的后继,首先节点的后继指针应该是N的后继,先这么做不至于链表断掉,然后再让N的后继换成ptr指向的节点,如果颠倒,那N的后继改变之后,就相当于将原来N的后继扔掉了,没有指针可以找到这一块内存,这块内存就泄漏了,这是很危险的,这也是一般不建议使用链表操作的原因。
两种方法一个思想,先用一个指针ptr创建节点,再用链表指针将这个节点串进去.

头插法

pnode ptr = (pnode)malloc(sizeof(node));
ptr->data = 100;//创建节点
ptr->next = LinkList->next;//后继是头节点的后继,即原来的最前端。
LinkList->next = ptr;//头节点指向新的最前端

很简单吼,当然,如果需要重复插入,循环就好了

尾插法相对的一气呵成,一般没有一次插一个的,因为如果想尾插,一般需要一个尾指针,但是我写的这个是不带尾指针的,所以尾插法更适合一气呵成直接创建一个长度为假设10的链表。
基本思路就是设计一个尾指针toil,toil指向的就是链表的最后一个节点,然后每次操作就操作这个节点,先用ptr创建一个节点之后,再让toil的后继插在ptr指向的节点上面就阔以了,当然最后需要后移toil指针。

int i;
pnode toil = LinkList;//刚开始链表最后一个节点是头节点。
pnode ptr = NULL;//指针初始化为空,尽量不要有产生野指针的机会
for(i = 0; i < 10; i++)
{
	ptr = (pnode)malloc(sizeof(node));
	ptr->data = 100;//创建节点。
	ptr->next = NULL;//这句如果写了实际没什么大用,主要还是为了避免产生野指针。
	toil->next = ptr;//尾指针的后继指向创建起来的指针
	toil = toil->next;//尾指针后移
	//这几句话能压行压到两行,你自己可以试一试
}
	toil->next = NULL;//这句纯属是给不写ptr->next = NULL的人写的,最后的指针一定要置空

尾插法相对复杂一点,但是也不算太复杂,而且基本成定式,照着写就OK

对于一些奇怪的结构,我们都可以在这些操作的基础之上建立起来,比如环形链表,那么就是本来我是要将最后一个节点的后继置空的,但是我没有,我将这根针直接插在了头节点上而已。

增删改查

链表的增删改查相对于数组而言要麻烦一些,链表不支持随机存储,所以要在定位上花费很多时间,但是我们为什么学呢,主要是因为这是一种结构,是其他结构实现的基础,而且不限制最大规模,还是灵活运用吧。
我们只对增删最前端的节点做一些说明
从前边添加一个节点即头插法创建链表的操作,不再作解释,我们介绍一下如何删除一个最前端的节点。

首先要说明,所有的节点都是靠动态内存分配的,内存相当于借的,有借有还,再借不难,程序结束之前,应该将所有的动态内存原数归还给计算机。
所以只要有malloc,就应该有free,这是一个好习惯。

所以删除之后的那个节点应该还给计算机,具体操作,我先使用一个指针ptr插到我想删除的节点上,这样节点被取出时至少我还知道他在哪。然后我直接像数组那样,不把他当个玩意看,头节点的后继指针直接绕过这个节点插到这个节点的后继上,然后再给这个节点还回去,大致就是这么个过程,具体操作如下

pnode ptr = LinkList->next;//指向我想删除的节点
LinkList->next = LinkList->next->next;//绕过这个节点
free(ptr);//释放内存

所以,在程序结束之后应该有个清空链表的操作,演示一哈

pnode ptr = NULL;
while(LinkList->next)//只要头节点后边还有东西
{
	ptr = LinkList->next;
	LinkList->next = LinkList->next->next;
	free(ptr);//就删除掉他
}
free(LinkList);//最后别忘了清除头节点

链表也有他的遍历操作,也比较简单,如果说数组的遍历是

int i;
for(i = 0; i < n; i++)

的话,那么链表的操作类比一下就可以得到

pnode ptr;
for(ptr = LinkList->next; ptr != NULL; ptr = ptr->next)

是不是很简单

最后,提醒孩子们,链表的插入删除操作如果想要写函数的话,记得传二重指针。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值