犹豫了几天,看了很多大牛写的关于c语言链表,感触很多,终于下定决心,把自己对于链表的理解随之附上,可用与否,自行裁夺。由于作者水平有限也是第一次写,不足之处,竭诚希望得到各位大神的批评指正。制作不易,不喜勿喷,谢谢!!!

在正文开始之前,我先对数组和链表进行简单的对比分析。

链表也是一种很常见的数据结构,不同于数组的是它是动态进行存储分配的一种结构。数组存放数据时,必须要事先知道元素的个数。举个例子,比如一个班有40个人,另一个班有100个人,如果要用同一个数组先后来存放这两个班的学生数据,那么必须得定义长度为100的数组。如果事先不确定一个班的人数,只能把数组定义的足够大,以能存放任何班级的学生数据。这样就很浪费内存,而且数组对于内存的要求必须是是连续的,数据小的话还好说,数据大的话内存分配就会失败,数组定义当然也就失败。还有数组对于插入以及删除元素的效率也很低这就不一一介绍了。然而链表就相对于比较完美,它很好的解决了数组存在的那些问题。它储存数据时就不需要分配连续的空间,对于元素的插入以及删除效率就很高。可以说链表对于内存就是随用随拿,不像数组要事先申请。当然,有优点就必然有缺点,就比如说链表里每一个元素里面都多包含一个地址,或者说多包含一个存放地址的指针变量,所以内存开销就很大。还有因为链表的内存空间不是连续的,所以想找到其中的某一个数据就没有数组那么方便,必须先得到该元素的上一个元素,根据上一个元素提供的下一元素地址去找到该元素。所以不提供“头指针”(下文中“头指针”为“PHead”),那么整个链表将无法访问。链表就相当于一条铁链一环扣一环(这个稍后会详细的说)。

链表

上面我提到过链表是动态进行存储分配的一种结构。链表中的每一个元素称为“结点”,每个结点都包括两部分:一部分为用户需要的实际数据,另一部分为下一结点的地址。链表有一个“头指针(PHead)”变量,存放着一个地址,该地址指向第一个结点,第一个结点里面存放着第二个结点的地址,第二个结点又存放着第三个结点地址。就这样头指针指向第一个结点,第一个结点又指向第二个......直到最后一个结点。最后一个结点不再指向其他结点,地址部分存放一个“NULL”。 见下图:(表中有一个尾指针(PEnd)其作用后面会解释)

c语言单项链表尾添加整体代码如下:(详解附后)

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h> //函数声明
//尾添加
void wei_tian_jia(struct NODE** PHEAD, struct NODE** PEND, int shu_ju);
//尾添加(没有尾指针)
void wei_tian_jia_(struct NODE** PHEAD, int shu_ju);
//释放链表
void shi_fang_lian_biao(struct NODE* PHEAD);
//释放链表(并是头指针(PHead)尾指针(PEnd)指向空)
void shi_fang_lian_biao_free(struct NODE** PHEAD, struct NODE** PEnd);
//输出链表
void shu_chu(struct NODE* PHEAD); //定义一个链表结构体
struct NODE
{
int shu_ju; //用户需要的实际数据
struct NODE* PNext; //下一结点的地址 }; int main(void)
{
//创建头尾指针
struct NODE* PHead = NULL;
struct NODE* PEnd = NULL; //尾添加
wei_tian_jia(&PHead, &PEnd, 17);
wei_tian_jia(&PHead, &PEnd, 21);
wei_tian_jia(&PHead, &PEnd, 34);
wei_tian_jia(&PHead, &PEnd, 8);
wei_tian_jia(&PHead, &PEnd, 24); //尾添加(没有尾指针)
//wei_tian_jia_(&PHead, 23);
//wei_tian_jia_(&PHead, 17);
//wei_tian_jia_(&PHead, 11); //输出链表
shu_chu(PHead); //释放链表
//shi_fang_lian_biao(PHead); //释放链表(并是头指针(PHead)尾指针(PEnd)指向空)
shi_fang_lian_biao_free(&PHead, &PEnd); system("pause");
return 0;
}


//尾添加
void wei_tian_jia(struct NODE** PHEAD, struct NODE** PEND, int SHU_JU)
{
//创建结点
struct NODE* PTEMP = (struct NODE*)malloc(sizeof(struct NODE));
if (PTEMP != NULL)
{
//节点赋值
PTEMP->shu_ju = SHU_JU;
PTEMP->PNext = NULL; //把结点连起来
if (NULL == *PHEAD) // 因为PHEAD如果是NULL的话 PEND也就是NULL 所以条件里面不必要写
{
*PHEAD = PTEMP;
*PEND = PTEMP;
}
else
{
(*PEND)->PNext = PTEMP;
*PEND = PTEMP; }
}
} //尾添加(没有尾指针)
void wei_tian_jia_(struct NODE** PHEAD1, int SHU_JU)
{
//创建结点
struct NODE* PTEMP = (struct NODE*)malloc(sizeof(struct NODE)); if (PTEMP != NULL)
{
//结点成员赋值
PTEMP->shu_ju = SHU_JU;
PTEMP->PNext = NULL; //把结点连一起
if (NULL == *PHEAD1)
{
*PHEAD1 = PTEMP;
}
else
{
struct NODE* PTEMP2 = *PHEAD1;
while (PTEMP2->PNext != NULL)
{
PTEMP2 = PTEMP2->PNext;
}
PTEMP2->PNext = PTEMP;
} } } //输出链表
void shu_chu(struct NODE* PHEAD)
{
while (PHEAD != NULL)
{
printf("%d\n", PHEAD->shu_ju);
PHEAD = PHEAD->PNext; } } //释放链表
void shi_fang_lian_biao(struct NODE* PHEAD)
{
struct NODE* P = PHEAD;
while (PHEAD != NULL)
{
struct NODE* PTEMP = P;
P = P->PNext;
free(PTEMP);
}
free(PHEAD); }
//释放链表(并是头指针(PHead)尾指针(PEnd)指向空)
void shi_fang_lian_biao_free(struct NODE** PHEAD, struct NODE** PEnd)
{ while (*PHEAD != NULL)
{
struct NODE* PTEMP = *PHEAD;
*PHEAD = (*PHEAD)->PNext;
free(PTEMP);
}
*PHEAD = NULL;
*PHEAD = NULL; }

部分代码详解:

(再次申明:由于作者水平有限,所以有的变量名用的拼音。见笑,莫怪!!!为了简单明了,方便起见,我定义了一个实际数据。)

“头指针”(PHead)以及“尾指针”(PEnd):

头指针很好理解指向首结点用于遍历整个数组,而尾指针呢?我们先看下面两段代码一段是有尾指针的一段是没有尾指针的:

 显然这是一段有尾指针的代码。这里的思想就是当写入第一个成员进链表的时候,此时链表就一个成员,即是头(PHEAD),也是尾(PEND),当写入第二个成员的时候,链表头(PHEALD)不动链表尾(PEND)向后移,指向最后一个结点。
//尾添加
void wei_tian_jia(struct NODE** PHEAD, struct NODE** PEND, int SHU_JU)
{
//创建一个结点
struct NODE* PTEMP = (struct NODE*)malloc(sizeof(struct NODE));
if (PTEMP != NULL)
{
//节点成员赋值(一定要每个成员都要赋值)
PTEMP->shu_ju = SHU_JU;
PTEMP->PNext = NULL; //把结点连起来
if (NULL == *PHEAD) // 因为PHEAD如果是NULL的话 PEND也就是NULL 所以条件里面不必要写
{
*PHEAD = PTEMP;
*PEND = PTEMP;
}
else
{
//把尾指针向后移
(*PEND)->PNext = PTEMP;
*PEND = PTEMP; } } }

那么下面这段代码是没有尾指针的。它的思想就是头指针一直指向第一个结点,然后通过遍历来找到最后一个结点,从而使最后一个结点里面的指针指向所要插入的元素。

//尾添加(没有尾指针)
void wei_tian_jia_(struct NODE** PHEAD1, int SHU_JU)
{
//创建结点
struct NODE* PTEMP = (struct NODE*)malloc(sizeof(struct NODE)); if (PTEMP != NULL)
{
//结点成员赋值
PTEMP->shu_ju = SHU_JU;
PTEMP->PNext = NULL; //把结点连一起
if (NULL == *PHEAD1)
{
*PHEAD1 = PTEMP; }
else
{
struct NODE* PTEMP2 = *PHEAD1;
while (PTEMP2->PNext != NULL)
{
PTEMP2 = PTEMP2->PNext;
}
PTEMP2->PNext = PTEMP;
} } }

我把上面代码里面的一段摘出来说明一下。

这段代码里面可以看到我又定义了一个PTEMP2指针变量,为什么呢?前面我提到过没有尾指针的时候添加结点的思想就是要遍历数组,从而找到最后一个结点然后让它指向我们要插入的结点,如果没有这个PHEAD2,我们遍历完链表以后我们的头指针PHEAD1就已经指向了最后一个结点了,单项链表如果头指针移动了,数据就会找不到了。所以我定义了一个中间变量装着头指针然后去遍历链表,让头指针永远指向链表的头。

        else
{
struct NODE* PTEMP2 = *PHEAD1;
while (PTEMP2->PNext != NULL)
{
PTEMP2 = PTEMP2->PNext;
}
PTEMP2->PNext = PTEMP;
}

可以看到有尾指针的代码和没有尾指针的代码里面,有尾指针的链表里面我每次添加完数据都让尾指针指向最后一个结点,然后通过尾指针来添加数据。而没有尾指针的链表里面每次添加数据都要通过循环来遍历链表找到最后一个结点然后指向所添加的结点。如果一个链表里面有几万个结点,每次都通过循环遍历链表来添加数据,那么速度就相对于有尾指针的链表慢很多。总而言之,还是看个人爱好吧。不管黑猫还是白猫能抓到耗子都是好猫。

#include <stdio.h>#include <stdlib.h>#include <malloc.h>

//函数声明//尾添加void wei_tian_jia(struct NODE** PHEAD, struct NODE** PEND, int shu_ju);//尾添加(没有尾指针)void wei_tian_jia_(struct NODE** PHEAD, int shu_ju);//释放链表void shi_fang_lian_biao(struct NODE* PHEAD);//释放链表(并是头指针(PHead)尾指针(PEnd)指向空)void shi_fang_lian_biao_free(struct NODE** PHEAD, struct NODE** PEnd);//输出链表void shu_chu(struct NODE* PHEAD);

//定义一个链表结构体struct NODE{int shu_ju;           //用户需要的实际数据struct NODE* PNext;   //下一结点的地址
};

int main(void){//创建头尾指针struct NODE* PHead = NULL;struct NODE* PEnd = NULL;

//尾添加wei_tian_jia(&PHead, &PEnd, 17);wei_tian_jia(&PHead, &PEnd, 21);wei_tian_jia(&PHead, &PEnd, 34);wei_tian_jia(&PHead, &PEnd, 8);wei_tian_jia(&PHead, &PEnd, 24);
//尾添加(没有尾指针)//wei_tian_jia_(&PHead, 23);//wei_tian_jia_(&PHead, 17);//wei_tian_jia_(&PHead, 11);
//输出链表shu_chu(PHead);
//释放链表//shi_fang_lian_biao(PHead);
//释放链表(并是头指针(PHead)尾指针(PEnd)指向空)shi_fang_lian_biao_free(&PHead, &PEnd);

 system("pause");return 0;}
//尾添加void wei_tian_jia(struct NODE** PHEAD, struct NODE** PEND, int SHU_JU){//创建一个结点struct NODE* PTEMP = (struct NODE*)malloc(sizeof(struct NODE));if (PTEMP != NULL){//节点赋值PTEMP->shu_ju = SHU_JU;PTEMP->PNext = NULL;
//把结点连起来if (NULL == *PHEAD) // 因为PHEAD如果是NULL的话 PEND也就是NULL  所以条件里面不必要写{*PHEAD = PTEMP;*PEND = PTEMP;}else{(*PEND)->PNext = PTEMP;*PEND = PTEMP;
}
}}
//尾添加(没有尾指针)void wei_tian_jia_(struct NODE** PHEAD1, int SHU_JU){//创建结点struct NODE* PTEMP = (struct NODE*)malloc(sizeof(struct NODE));
if (PTEMP != NULL){//结点成员赋值PTEMP->shu_ju = SHU_JU;PTEMP->PNext = NULL;
//把结点连一起if (NULL == *PHEAD1){*PHEAD1 = PTEMP;}else {struct NODE* PTEMP2 = *PHEAD1;while (PTEMP2->PNext != NULL){PTEMP2 = PTEMP2->PNext;}PTEMP2->PNext = PTEMP;}
}
}
//输出链表void shu_chu(struct NODE* PHEAD){while (PHEAD != NULL){printf("%d\n", PHEAD->shu_ju);PHEAD = PHEAD->PNext;
}
}
//释放链表void shi_fang_lian_biao(struct NODE* PHEAD){struct NODE* P = PHEAD;while (PHEAD != NULL){struct NODE* PTEMP = P;P = P->PNext;free(PTEMP);}free(PHEAD);
}//释放链表(并是头指针(PHead)尾指针(PEnd)指向空)void shi_fang_lian_biao_free(struct NODE** PHEAD, struct NODE** PEnd){while (*PHEAD != NULL){struct NODE* PTEMP = *PHEAD; *PHEAD = (*PHEAD)->PNext;free(PTEMP);}*PHEAD = NULL;*PHEAD = NULL;

}

关于c语言单项链表尾添加的更多相关文章

  1. C语言之链表

    这两天在复习C语言的知识,为了给下个阶段学习OC做准备,以下的代码的编译运行环境是Xcode5.0版本,写篇博文把昨天复习的C语言有关链表的知识给大家分享一下,以下是小菜自己总结的内容,代码也是按照自 ...

  2. 2分钟 sublime设置自动行尾添加分号并换行:

    18:03 2016/4/162分钟 sublime设置自动行尾添加分号并换行:注意:宏文件路径要用反斜杠/,2个\\会提示无法打开宏文件.不需要绝对路径很简单利用宏定义:1.录制宏:由于是录制动作宏 ...

  3. sed命令给文本文件的每行的行首或者行尾添加文字

    在每行的头添加字符,比如"HEAD",命令如下: sed 's/^/HEAD&/g' test.file 在每行的行尾添加字符,比如“TAIL”,命令如下: sed 's/ ...

  4. C语言习题 链表建立,插入,删除,输出

    Problem B: C语言习题 链表建立,插入,删除,输出 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 222  Solved: 92 [Subm ...

  5. sed在行首或者行尾添加内容

    原文地址:http://www.cnblogs.com/ITEagle/archive/2013/06/20/3145546.html 用sed命令在行首或行尾添加字符的命令有以下几种: 假设处理的文 ...

  6. 在C语言结构体中添加成员函数

    我们在使用C语言的结构体时,经常都是只定义几个成员变量,而学过面向对象的人应该知道,我们定义类时,不只是定义了成员变量,还定义了成员方法,而类的结构和结构体非常的相似,所以,为什么不想想如何在C语言结 ...

  7. vim 在行首 行尾添加字符

    在行首添加字符: %s/^/your_word/ 在行尾添加字符 %s/$/your_word/

  8. c++刷题(27/100)反转单项链表,链表的倒数第k个

    题目1:调整数组顺序使奇数位于偶数前面 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位 ...

  9. notepad++ 行首行尾添加字符

    有一次要处理SQL,拿到了脚本.但是要将其写入java 代码中,要在行首和行尾添加上引号.利用notepad++进行编辑. $表示行尾,^表示行首. 如上图,就这样.很高效. 如果只是在行尾添加字符, ...

随机推荐

  1. symfony框架中使用service

    在config文件里面的service.yml写入自己service 1 chat.group_list: //service的名字 2 class: Chat\Service\GroupListSe ...

  2. 使用celery异步发送短信

    目录 1.使用celery异步发送短信 1.1在celery_task/mian.py中添加发送短信函数 1.2在verifcations/views.py中添加celery发送短信视图函数 1.3 ...

  3. SpringSecurity之整合JWT

    SpringSecurity之整合JWT 目录 SpringSecurity之整合JWT 1. 写在前面的话 2. JWT依赖以及工具类的编写 3. JWT过滤器 4. 登录成功结果处理器 5. Sp ...

  4. JZOJ 2020.10.6 【NOIP2017提高A组模拟9.7】简单无向图

    简单无向图 题目 Description Input Output Sample Input 输入1: 4 2 1 1 2 输入2: 10 2 2 2 2 1 1 2 1 1 2 Sample Out ...

  5. 将Shiny APP搭建为独立的桌面可执行程序 - Deploying R shiny app as a standalone application

    目录 起源! 目的? 怎么做? 0 准备工作 1 下载安装R-portable 2 配置 Rstudio 3 搭建Shiny App 3.1 添加模块 3.2 写AppUI和AppServer 3.3 ...

  6. python初次接触

    1.python有什么用或者能做什么? 可以做网站(比如 YouTube.豆瓣),可以做图片处理,可以做科学计算,也可以爬虫,甚至于游戏,学好Python后不用担心没有用武之地,Google就大量的在 ...

  7. iNeuOS工业互联平台,WEB组态(iNeuView)图元和数据点组合及生成新图元复用,实现拖业务

    目       录 1.      概述... 1 2.      平台演示... 2 3.      应用过程... 2 1.   概述 iNeuView视图建模Web组态平台实现图元和数据点组合及 ...

  8. 第6.3节 Python动态执行之动态编译的compile函数

    Python支持动态代码主要三个函数,分别是compile.eval和exec.本节介绍compile函数的语法和相关使用.compile函数用来编译一段字符串的源码,将其编译为字节码或者AST(抽像 ...

  9. Python中使用百分号占位符的字符串格式化方法中%s和%r的输出内容有何不同?

    Python中使用百分号占位符的字符串格式化方法中%s和%r表示需要显示的数据对应变量x会以str(x)还是repr(x)输出内容展示. 关于str和repr的关系请见: <Python中rep ...

  10. 老猿学5G随笔:5G网元功能体NF以及NF之间的两种接口--服务化接口和参考点

    一.5G功能体之间的接口类型 5G不同功能体之间提供了两种接口: 服务化接口:Service-basedinterface,这个是类似微服务化架构的服务注册和服务发现来实现的功能体对外暴露的接口,这种 ...