什么是链表(C语言)(2020.11)

前言

之前写了一篇博客什么是链表(Java)是用Java解释的链表,收获了对我个人而言还算不错的阅读和收藏。但还有一些解释的不太清晰的地方,这几天在学C,感觉用C来解释一些问题更清晰一些,另外就是对上一篇中的疑问进行解释,本来打算在上一篇下面补充的,但是感觉还是分开写更清晰些(本篇文章作为Java版的下篇)。

正文

首先看一下节点的定义

typedef struct node
{ ElemType data;		// 数据域
  struct node *next;	//指针域
}SLinkNode;

我相信很多同学在看到节点的定义代码时会比较疑惑(比如我,哈哈哈),在一个结构内部又定义了一个相同的结构,这让人很难理解;在一个node内部再定义一个node类型,这时候我就会想,这到底是一种包含的关系还是一种啥关系呢,毕竟指针域就定义在node定义里面,很难让人不这么想。

还有很多资料是用图来表示链表的,这个链接的箭头从哪里开始,链接到下个节点的哪个位置,这些细节都很关键,每一个细节都很容易误导读者,但是用C语言的指针来解释就会比较容易理解。

我们常见的链表都是这么画的,我们可以很直观的看的是一个链状的
加粗样式
但是在node节点里面再定义一个node是啥情况,难道是第一个里面包着第二个吗,如果这样的话,第二个里面有包着第三个,那岂不是第一个里面要包着第一个,第二个等等等等,
那岂不是要变成下面这个样子。
在这里插入图片描述
答案是否定的,是用C语言的指针结合一个常见的栗子能够很好的解决这个疑惑

要点是,不要用经常见到的指向,因为这个指向太抽象了,不容易理解,还有经常见到的上面那样的图片,到底是从一个节点的指针域指向下一个节点,还是从整个节点指向下一个节点,指向下一个节点的哪里呢,指向下一个节点的整体还是下一个节点的指针域呢。所以这样不好理解,可以跟着下面的例子尝试理解一下

首先回顾一下变量
不要简单的认为变量就是会变的量,借用一个经典的栗子,教室变不变,变,因为教室里面的人一直在变,但又不变,因为教室就在那里,a135永远是a135,不会前进也不会后退。有一个可变的内容和固定的地址,这就是变量。一般情况下,我们只关注变量的内容,不关注变量的地址。

然后回顾一下指针
指针就是一个普通的变量,只不过存的是一个变量的地址。虽然指针在声明时也有特定的类型,但是这个类型仅仅是针对指针所指向位置的类型,跟指针本身的大小没有关系,指针本身的大小在特定的操作系统中是固定的,详情可见我的另一篇博客一个int型的指针占几个字节,跟这个指针的类型有关系吗?

然后回顾一下结构体
结构体(C++中class的前身)相当于好几个屋子组成的房子,城市里有很多一模一样的房子,每个房子里有一个专门的屋子来存放下一个房子的地址(门牌号)。当你根据门牌号找到了第一个房子的位置,拿到了你想要的东西(数据域),然后找到了存放下一个房子门牌号的屋子,根据拿到的门牌号走向下一个房子(p = p -> next),以此类推,直到走的了某个房子里,发现这个房子用来存放下个房子门牌号的屋子里的内容为空(p -> next == NULL),到此,链表的遍历就结束了。(ps:用门牌号可以更好地理解指针,因为仅仅是存放的门牌号,所以只需要一个普通的地方就行,所以第一个房子里面不用包含着第二个房子)

根据这样的理解,关于指针的指针等等也就容易理解了。如下图所示,指针就是存放了一个变量地址的变量,指针的指针也是存放的一个地址,不过是一个指针的地址罢了。

char a = 1;
char *b = &a;
char **c = &b;

在这里插入图片描述

接下来看一下初始化链表的代码

void InitList(SLinkNode *&L) {
	L = (SLinkNode *)malloc(sizeof(SLinkNode));
	L->next = NULL;
}

节点是在需要的时候一个一个的添加上去的

然后看一下整体建表法(尾插法,根据一个有n个元素的数组a,由它来创建链表。)

void GreateListR(SLinkNode *&L, ElemType a[], int n) {
	SLinkNode *s, *tc; int i;
	L = (SLinkNode *)malloc(sizeof(SLinkNode));			//创建头节点
	tc = L;												//tc 始终指向尾节点,初始时指向头节点
											
	for (i = 0; i < n; i++) {
	s = (SLinkNode *)malloc(sizeof(SLinkNode));
	s->data = a[i];										//创建存放a[i]元素的新节点s
	tc->next = s;										//将s插入tc之后
	tc = s;
	}
	
	tc->next = NULL;									//尾节点next域置为NULL
}

另外就是有的资料里还有一个专门的头节点,有的没有,当时搜索资料学习的我就比较懵,到底哪个是对的呢,现在我来告诉你答案,单链表分为带头节点和不带头节点两种类型,所以都对,不过,在多数情况下,带有头节点的链表会简化运算的实现过程。

欢迎大家点赞留言讨论,一起加油鸭!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值