单链表前言
在我之前的一片文章当中已经介绍过了单链表的作用以及他的优缺点。对于一个单链表的基础操作,也在上篇文章中给予了大概的轮廓。这篇文章主要是介绍单链表的初始化。
目前这个系列的文章目录:
通过结构体定义节点
单链表的基本组成单位是节点,所以在编程当中我们先要定义这个节点。而这个节点主要内容包含了两个变量,一个是数据,另一个是指向下一个节点的指针。如果要想把两个变量放到一起,在C语言当中唯一的做法只有数据结构。或许有朋友会说用一个长度为2的数组也可以实现。这个说法不全,当数据类型是整型的时候,是可以的,不过倘若数据类型是字符,那么这个说法就实行不了了。
节点的定义有以下两种方法:
方法一:
struct node {
int value;
struct node* next;
};
方法二:
typedef struct node {
int value;
struct node* next;
}node;
如果在网上查资料的同学还会发现有一种写法,
方法三:
struct node{
int value;
struct node* next;
}node;
这三个方法很容易会让人混淆。我在自学阶段也研究了半天。方法三跟方法一的不同就是在“{}”后面多了一个“node”。而方法三看上去又跟方法二很像,所以很多时候就会觉得很迷惑。
其实方法一和方法三是一致的。
这么看,在方法一中,struct是说明现在要定义一个结构体。然后struct后面那一node就是来给这个结构体一个名字,名字就叫做node。然后这个结构体(node)的内容都在{}里面。当定义完之后node{}就已经成为了一中数据类型了,跟‘int’,'char’是在同一个范畴里的。然后在{}后面加上了一个名称(这里是node),其实和int a,是一个概念。所以在方法三中,它不仅仅是定义了一个结构体,而且同时创建了一个结构体变量。而方法一仅仅只是创建了一个结构体。代码大概框架如下:
struct //声明一个结构体
node //给结构命名
{ //给结构体node定义内容
int value;
struct node* next;
}
node; //创建一个数据类型为node的变量。
不过,方法一或者三中都有一个 缺点,就是每次创建一新的节点/结构体时,都要这样定义:
struct node a//a为任意你想取的名称
对于有成千上万行的程序来说,每次都要多打一次struct实在是太费劲了,所以就有了方法二这个代码。
typedef是一个给数据类型重新命名的指令。
例如typedef int a
从此在代码时,int c等同于 a c。
所以方法二中是这么理解的:
typedef //重新命名
struct node { //重新命名的对象
int value;
struct node* next;
}
node; //新名字
所以给结构体重新命名后,以后创建一个新的节点只需这样:
node p;
//它等同于
struct node p;
节点内容
节点的内容之前已经介绍个,在单链表里存放的信息主要有两项,一个数据,另一个就是指向下一个节点的指针(其实就是链)。在本文当中数据类型假设为int,当然有时候可能会需要char或者是其他类型的话,需要整体修改会很麻烦。在这里有另一种优化方式,就是我刚才说的typedef。
typedef int datatype
typedef struct node {
datatype value;
struct node* next;
}node;
目前为止一个新的数据结构就已经被成功定义了。下一步就是如何把数据放入到链表当中
生成链表头
链表头是整个链表的第一个节点,它不存放任何数据信息,不过它可以告诉我们第一个数据是从哪一个开始。
//---------------------------------------------------------------
//创建头标
printf("正在创建头链表......\n\n");
node* head = _head();
printf("头链表创建成功。\n\n");
而_head函数代码为:
//创建头标
node* _head() {
node* head = (node*)malloc(sizeof(node));
if (head == NULL)
exit(-1);//head=_head();
head->next = NULL;
return head;
}
在生成链表头当中主要是想要避免一个情况,就是head刚好被分配到了NULL这个地址,那么这个代码就运行不了了,因为我之前也说过,NULL这个地址是不能被访问的。
数据放入链表当中
在这个案例思虑的比较简单,就是假设我们已经知道数据量的大小以及我们人工输入数据。在一般的情况下数据量很大的话,可以直接应用文件fopen来读取数据。不过这里就先引用一个稍微简单的方法,由浅入深。以下是在主函数内的代码。
//---------------------------------------------------------------
//输入初始化数据个数
printf("多少个数字你想输入?(输入数字)\n");
scanf_s("%d", &num);
head->value = num;
//---------------------------------------------------------------
//插入数据
printf("\n开始输入你想输入的数字。\n");
_insert(head, num);
printf("\n完成输入,链表初始化完成。\n\n");
在这里可以看到,我把数据量的大小直接寄存在了头文件的数据域里,其实一般情况下数据域是不存在任何信息的,不过为了之后的步骤,暂时先在这里记录链表的尺寸吧。
对于函数:_insert(head,num);它的代码如下:
//----------------------------------------------------------
//插入新数据
void _insert(node* head, int num) {
int v, i = 0;
while (i < num) {
node* current = head;
node* new = (node*)malloc(sizeof(node));
printf("第 %d 位数据数值: ", i + 1);
scanf_s("%d", &v);
char c = getchar();
new->value = v;
new->next = NULL;
while (current->next != NULL) {
current = current->next;
}
current->next = new;
i++;
}
}
在这代码比较直接关注的部分是:
new->value = v;
new->next = NULL;
while (current->next != NULL) {
current = current->next;
}
current->next = new;`
在这个代码当中,对于初学者理解可能有一些吃力,以下为它创立的顺序步骤图:
对于while循环里面,其实就是想找到这个链表目前为止的最后一个节点,找到之后就把最后一个节点里的指针指向新节点以求达到新节点被添加到链表里的效果。
当以上步骤都完成之后,你那么一个单链表的初始化就已经被完成了。之后的文章会开始讲解不同的操作,例如打印,查询和定位功能。