C语言中链表的创建(本文中采用的尾插法)
声明:此文链表采用的增删改查函数皆以传递结构体中的数据为主,传递结构体的指针后续我将另做文章.
结构体
创建一个链表,首先就要创建一个结构体,来承载我们的数据;
因为本文中采用的是尾插法,结构体创建如下:
typedef struct link{
int data;
struct link* next;//用于连接下一节点
}link;
头尾指针
创建好结构体后,我们需要头尾指针,用于后续一系列的增删改查操作,
头尾指针创建如下:
//声明并定义头尾指针,用于后续调用
struct node* head = NULL;
struct node* end = NULL;
声明增删改查的函数
增删改查的四个操作分别需要对应的函数,而本文中将查分为了两个,一个是查询节点所在位置,另一个是查询全部节点并打印. 前者能返回该节点,有利于我们的增删改的操作更为简便.
下面是这些函数名称:
int node_add(int a);//添加节点;
void node_check();//查询全部的节点并打印出来;
struct node* node_check_point(int a);//查询节点并返回节点指针;
void link_delete();//删除全部节点;
void node_input(int place, int data);//在指定位置插入节点;
void node_change(int data, int change_Data);//改变指定节点的数据;
首先来说一下:
"增"函数: node_add(int data):
代码如下:
//添加节点
int node_add(int data){
struct node* temp = (struct node*)malloc(sizeof(struct node));
if (temp == NULL){
printf("开辟地址空间失败\n");
return 0;
}
temp->a = data;
temp->next = NULL;
if (head == NULL)//如果此时链表中没有节点,就让这个新添加的temp成为头结点;
{
head = temp;
}
else//如果链表中已经有了节点,则这个新添加的temp成为下一节点,也就是让end的下一节点指向这一temp节点;
{
end->next = temp;
}
end = temp;//最后再让temp成为end节点;下一次再调用这个函数时,此函数中的end已经指向了temp;只是表面上名称没改变而已,怕有人混淆,特此说明;
return 0;
}
要进行节点的添加操作,首先要判断原链表中是否存在一个节点,如果不存在节点,也就是head指针指向的是一片空地址时,我们就需要单独判断并使我们要添加的节点成为头结点;如果存在任一节点,我们就可以直接在其后边添加节点,函数中的每一段代码我已添加详细注释说明.
"删"函数: link_delete()
代码如下:
void link_delete(){
struct node* lb = head;
while (lb != NULL){
node* lb_p = lb;
lb = lb->next;
free(lb_p);
}
head = NULL;
end = NULL;
}
删除全部节点比较简单,只需要创建一个结构体指针指向我们的头指针(head),然后利用这个新创建的指针来查并删除全部的节点.
注意
我们删除完全部节点后,此时链表已经为空,我们需要让我们的头尾指针指向空地址,也就是空指针.
"查"函数1:link_check_point(int a)
代码如下:
//查询指定节点中的数据,返回该节点
struct node* node_check_point(int a){
struct node* lb = head;
while (lb != NULL){
if (a == lb->a){
return lb;
}
lb = lb->next;
}
printf("该数据不存在于任一节点中!");
return NULL;
}
这个link_check_point函数能让我们找到对应数据所处的节点,并返回这一节点,这样我们在后续的删除指定节点或者修改指定节点时就能十分方便的调用.
"查"函数2:node_check()
代码如下:
void node_check(){
struct node* lb = head;//将头结点的指针赋值给lb,从而可以开始遍历链表;
while (lb != NULL){
printf("%d\n", lb->a);
lb = lb->next;
}
}
这个函数仅仅只是用于查遍节点并打印全部节点.无其他功能.
"改"函数:node_change
代码如下:
//改指定节点
void node_change(int data, int change_Data){
struct node* temp = node_check_point(data);//调用前面的查询节点函数,找到要改的数据所在的节点;
temp->a = change_Data;
return;
}
这里就用到上面的node_check_point函数,直接查询到对应的节点再用于修改节点值,非常的银杏.
以及最后一个函数:在指定位置添加节点:
//在指定位置插入节点
void node_input(int place,int data){
//先判断链表中有无节点
if (head == NULL){
printf("链表中无节点,无法插入!");
return;
}
node* node_point = head;
while (node_point->a != place){
node_point = node_point->next;
if (node_point == NULL){
printf("要插入位置的节点不存在,无法插入!\n");
return;
}
}
struct node* temp;//创建要插入的节点;
temp = (struct node*)malloc(sizeof(struct node*));
if (temp == NULL){
printf("申请地址空间失败!\n");
}
temp->a = data;
temp->next = NULL;
if (node_point == end){
end->next = node_point;
end = node_point;
return;
}
//到此步时已找到要插入位置的节点,开始插入;
temp->next = node_point->next;
node_point->next = temp;//插入完成;
}
为了利于更多初学者理解链表的操作原理,这里我没有调用查询节点函数,再次利用临时指针查询节点来找到要插入位置的节点.
下面奉上最后的函数的检验:
int main(int argc, char* argv[]){
node_add(1);
node_add(2);
node_add(3);
node_check();//此时结果为1 2 3
node_input(2, 6);//在2后插入一个节点,节点中的数据为6
node_check();//此时结果为 1 2 6 3
node_change(6, 5);//将节点中数据6改为数据5
node_check();//此时结果为 1 2 5 3
return 0;
}
总结
我们在进行增删改查一系列操作时,常常需要遍历链表,一般的我们不会头结点或传递的节点直接进行操作,这时需要我们临时创建一个指针,指向我们的链表头部来进行遍历操作.
在使用malloc开辟地址空间时,我们要注意地址空间申请失败的情况.
另外在最后使用完指针不再需要后,要及时释放地址空间.
(注:如果多次使用了malloc函数,则更需注意开辟空间失败的情况,这时应在返回前及时释放掉指针.)
我同样作为一个初学者,外加边写边改了部分代码,文中可能会有说错或有不足之处,往各位大佬的你们或同为初学者的你们指出,共同进步!