犹豫了几天,看了很多大牛写的关于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. jmeter接口测试多数据组合登陆场景

    一.安装好Java运行环境 百度下载JDK并且配置JAVA环境的教程一搜一大把,这里我就不详说了 二.运行JMETER 打开安装目录的bin文件中的jmeter.bat文件 三.添加程序 1.添加线程 ...

  2. Java命令行启动jar包更改默认端口以及配置文件的几种方式

    Java命令行启动jar包更改默认端口以及配置文件的几种方式 java -jar xxx.jar --server.port=8081 默认如果jar包没有启动文件,可以采用这种方式进行启动 java ...

  3. 2017年第八届蓝桥杯【C++省赛B组】B、C、D、H 题解

    可能因为我使用暴力思维比较少,这场感觉难度不低. B. 等差素数列 #暴力 #枚举 题意 类似:\(7,37,67,97,127,157\) 这样完全由素数组成的等差数列,叫等差素数数列. 上边的数列 ...

  4. F - LCS 题解(最长公共子序列记录路径)

    题目链接 题目大意 给你两个字符串,任意写出一个最长公共子序列 字符串长度小于3e3 题目思路 就是一个记录路径有一点要注意 找了好久的bug 不能直接\(dp[i][j]=dp[i-1][j-1]+ ...

  5. linux设置共享文件夹 - samba

    安装samba sudo apt-get install samba 配置 /etc/samba/smb.conf 的global模块添加security = user 最下加入 [share] pa ...

  6. 【mq读书笔记】消息拉取

    疑问:PullRequest何时添加? PullMessageService提供延迟添加与立即添加2种方式 疑问:PullRequest是在什么时候创建的呢? 1.上上图中 PullRequest p ...

  7. 色相偏移 HueShift ASE

    色相偏移可以改变颜色色调,unity ASE没有参考UE4写个,原理很简单,将颜色向量绕(1,1,1)旋转,就可以得到不同色调的颜色. https://zhuanlan.zhihu.com/p/677 ...

  8. PyQt+moviepy音视频剪辑实战1:多个音视频合成顺序播放或同屏播放的视频文件实现详解

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt+moviepy音视频剪辑实战 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 一. ...

  9. 在Centos7下docker配置自动化环境镜像(python3.7+selenium 3.11+firefox 62+geckodriver 0.21)

    最近在学习Docker,准备做自动化测试代码集成的功能.如下文章的前提是已经安装好linux系统,且成功安装好Docker. 接下来我会按步骤一步一步的对自动化需要的一些环境进行安装,如果没有特别说明 ...

  10. selenium模拟淘宝登陆,过所有验证

    淘宝模拟登陆实现 由于淘宝使用了滑动验证码,需要进行模糊手动滑动,因此考虑使用selenium+chromedriver进行模拟登陆. 淘宝的登陆网址:https://login.taobao.com ...