一:链表基础

1、链表基础----------------摘录自百度百科

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。链表可以在多种编程语言中实现。像Lisp和Scheme这样的语言的内建数据类型中就包含了链表的存取和操作。程序语言或面向对象语言,如C,C++和Java依靠易变工具来生成链表。

 
2、链表特点

线性表的链式存储表示的特点是用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。因此,为了表示每个数据元素与其直接后继数据元素 之间的逻辑关系,对数据元素 来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。由这两部分信息组成一个"结点",表示线性表中一个数据元素。线性表的链式存储表示,有一个缺点就是要找一个数,必须要从头开始找起,十分麻烦,不能像数组那样随机访问。
对于非线性的链表,可以参见相关的其他数据结构,例如树、图。另外有一种基于多个线性链表的数据结构:跳表,插入、删除和查找等基本操作的速度可以达到O(nlogn),和平衡二叉树一样。
其中存储数据元素信息的域称作数据域(设域名为data),存储直接后继存储位置的域称为指针域(设域名为next)。指针域中存储的信息又称做指针或链。
由分别表示,,…,的N 个结点依次相链构成的链表,称为线性表的链式存储表示,由于此类链表的每个结点中只包含一个指针域,故又称单链表或线性链表。
 
3、循环链表、双链表、单链表和顺序表

循环链表是与单链表一样,是一种链式的存储结构,所不同的是,循环链表的最后一个结点的指针是指向该循环链表的第一个结点或者表头结点,从而构成一个环形的链。
循环链表的运算与单链表的运算基本一致。所不同的有以下几点:
(1)在建立一个循环链表时,必须使其最后一个结点的指针指向表头结点,而不是象单链表那样置为NULL。此种情况还使用于在最后一个结点后插入一个新的结点。
(2)在判断是否到表尾时,是判断该结点链域(指针域)的值是否是表头结点,当链域值等于表头指针时,说明已到表尾。而非象单链表那样判断链域值是否为NULL。
双向链表
双向链表其实是单链表的改进。
当我们对单链表进行操作时,有时你要对某个结点的直接前驱进行操作时,又必须从表头开始查找。这是由单链表结点的结构所限制的。因为单链表每个结点只有一个存储直接后继结点地址的链域,那么能不能定义一个既有存储直接后继结点地址的链域,又有存储直接前驱结点地址的链域的这样一个双链域结点结构呢?这就是双向链表。
在双向链表中,结点除含有数据域外,还有两个链域,一个存储直接后继结点地址,一般称之为右链域;一个存储直接前驱结点地址,一般称之为左链域。
 
4、链表包含以下特征:(1).由n个节点离散分配;(2).每个节点通过指针链连接(3)每一个节点由一个前驱节点和一个后驱节点(4).首节点没有前驱节点,尾节点没有后驱节点;满足上面的4条,我们就称为链表;链表既然由很多个节点,那节点又由什么组成?节点由两个部分组成,一是数据域,用来存放有效数据;二是指针域,用来指向下一个节点

二、单链表
1、
           
2、节点的构建
typedef  struct Node
{
int data;//数据域
struct Node *pNext;//指针域   定义一个结构体指针,指向下一次个与当前节点数据类型相同的节点  
}Node,*pNode;
3、链表的创建

在创建链表之前,我们需要需要了解一下专业术语:

 首节点:存放第一个有效数据的节点;

尾节点:存放最后一个有效数据的节点;

 头节点:头节点的数据类型与首节点的数据类型相同,并且头节点是首节点前面的那个节点,并不存放有效数据;头节点的存在只是为了方便链表的插入和删除等操作。

头指针:指向头节点的指针;

尾指针:指向尾节点的指针;

参考博客:http://blog.csdn.net/lpp0900320123/article/details/20356143
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

1、数据元素的存储结构形式有两种:顺序存储和链式存储。
顺序存储结构:是把数据元素存放在地址连续的存储单元里,其数据间的逻辑关系和物理关系是一致的。
链式存储结构:是把数据元素存放在任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的。
很显然,这样说的话链式存储结构的数据元素存储关系并不能反映其逻辑关系,因此需要用一个指针存放数据元素的地址,这样子通过地址就可以找到相关联数据元素的位置。
2、数据元素之间的关系:
(1)集合关系
(2)线形关系
(3)树形关系
(4)图形关系
3、
算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。对于给定的问题,是可以有多种算法来解决的。
算法具有五个基本特征:输入、输出、有穷性、确定性和可行性。
(1)输入
算法具有零个或多个输入。
尽管对于绝大多数算法来说,输入参数都是必要的。但是有些时候,像打印“I love fishc.com”,就不需要啥参数啦。
(2)输出
算法至少有一个或多个输出。
算法是一定要输出的,不需要它输出,那你要这个算法来干啥?
输出的形式可以是打印形式输出,也可以是返回一个值或多个值等。

(3)有穷性
指算法在执行有限的步骤之后,自动结束而不会出现无限循环,并且每一个步骤在可接受的时间内完成。
一个永远都不会结束的算法,我们还要他来干啥?

(4)确定性
算法的每一个步骤都具有确定的含义,不会出现二义性。
算法在一定条件下,只有一条执行路径,相同的输入只能有唯一的输出结果。
算法的每个步骤都应该被精确定义而无歧义。

(5)可行性
算法的每一步都必须是可行的,也就是说,每一步都能够通过执行有限次数完成。

好的算法应该具备时间效率高和存储量低的特点。

4、
算法效率一般是指算法的执行时间,判断一个算法的效率时,函数中的常数和其他次要项常常可以忽略,而更应该关注主项(最高项)的阶数。
5、
算法时间复杂度的定义:在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。
算法的时间复杂度,也就是算法的时间量度,记作:T(n)= O(f(n))。
它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称为时间复杂度。其中f(n)是问题规模n的某个函数。

一般情况下,随着输入规模n的增大,T(n)增长最慢的算法为最优算法。

6、怎么推导大O阶呢?
用常数1取代运行时间中的所有加法常数。
在修改后的运行次数函数中,只保留最高阶项。
如果最高阶项存在且不是1,则去除与这个项相乘的常数。
得到的最后结果就是大O阶。

[线性阶]

一般含有非嵌套循环涉及线性阶,线性阶就是随着问题规模n的扩大,对应计算次数呈直线增长。

int i , n = 100, sum = 0;

for( i=0; i < n; i++ )
{
sum = sum + i;
}

上面这段代码,它的循环的时间复杂度为O(n),因为循环体中的代码需要执行n次。

[平方阶]

刚才是单个循环结构,那么嵌套呢?

int i, j, n = 100;

for( i=0; i < n; i++ )
{
for( j=0; j < n; j++ )
{
printf(“I love FishC.comn”);
}
}

n等于100,也就是说外层循环每执行一次,内层循环就执行100次,那总共程序想要从这两个循环出来,需要执行100*100次,也就是n的平方。所以这段代码的时间复杂度为O(n^2)。

那如果有三个这样的嵌套循环呢?

没错,那就是n^3啦。所以我们很容易总结得出,循环的时间复杂度等于循环体的复杂度乘以该循环运行的次数。

刚刚我们每个循环的次数都是一样的,如果:

int i, j, n = 100;

for( i=0; i < n; i++ )
{
for( j=i; j < n; j++ )
{
printf(“I love FishC.comn”);
}
}

分析下,由于当i=0时,内循环执行了n次,当i=1时,内循环则执行n-1次……当i=n-1时,内循环执行1次,所以总的执行次数应该是:
n+(n-1)+(n-2)+…+1 = n(n+1)/2

大家还记得这个公式吧?恩恩,没错啦,就是搞死先生发明的算法丫。
那咱理解后可以继续,n(n+1)/2 = n^2/2+n/2
用我们推导大O的攻略,第一条忽略,因为没有常数相加。第二条只保留最高项,所以n/2这项去掉。第三条,去除与最高项相乘的常数,最终得O(n^2)。

[对数阶]

对数,属于高中数学内容啦,对于有些鱼油可能对这玩意不大理解,或者忘记了,也没事,咱分析的是程序为主,而不是数学为主,不怕。

int i = 1, n = 100;

while( i < n )
{
i = i * 2;
}

由于每次i*2之后,就距离n更近一步,假设有x个2相乘后大于或等于n,则会退出循环。
于是由2^x = n得到x = log(2)n,所以这个循环的时间复杂度为O(logn)。

7、

头指针

头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。

头指针具有标识作用,所以常用头指针冠以链表的名字(指针变量的名字)。

无论链表是否为空,头指针均不为空。

头指针是链表的必要元素。

头结点

头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(但也可以用来存放链表的长度)。

有了头结点,对在第一元素结点前插入结点和删除第一结点起操作与其它结点的操作就统一了。

头结点不一定是链表的必须要素。

单链表图例:

单链表图例

空链表图例:

空链表图例

获得链表第i个数据的算法思路:

声明一个结点p指向链表第一个结点,初始化j从1开始;

当j<i时,就遍历链表,让p的指针向后移动,不断指向一下结点,j+1;

若到链表末尾p为空,则说明第i个元素不存在;

否则查找成功,返回结点p的数据。

 

单链表的插入

我们先来看下单链表的插入。假设存储元素e的结点为s,要实现结点p、p->next和s之间逻辑关系的变化,大家参考下图思考一下:

单链表的插入

我们思考后发觉根本用不着惊动其他结点,只需要让s->next和p->next的指针做一点改变。

s->next = p->next;

p->next = s;

我们通过图片来解读一下这两句代码。

单链表的插入

那么我们考虑一下大部分初学者最容易搞坏脑子的问题:这两句代码的顺序可不可以交换过来?

先 p->next = s;

再 s->next = p->next;

大家发现没有?如果先执行p->next的话会先被覆盖为s的地址,那么s->next = p->next其实就等于s->next = s了。

所以这两句是无论如何不能弄反的,这点初学者一定要注意咯~

单链表第i个数据插入结点的算法思路:

声明一结点p指向链表头结点,初始化j从1开始;

当j<1时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1;

若到链表末尾p为空,则说明第i个元素不存在;

否则查找成功,在系统中生成一个空结点s;

将数据元素e赋值给s->data;

单链表的插入刚才两个标准语句;

返回成功。

代码示例:

单链表的删除

现在我们再来看单链表的删除操作。

单链表的删除

假设元素a2的结点为q,要实现结点q删除单链表的操作,其实就是将它的前继结点的指针绕过指向后继结点即可。

那我们所要做的,实际上就是一步:

可以这样:p->next = p->next->next;

也可以是:q=p->next; p->next=q->next;

单链表第i个数据删除结点的算法思路:

声明结点p指向链表第一个结点,初始化j=1;

当j<1时,就遍历链表,让P的指针向后移动,不断指向下一个结点,j累加1;

若到链表末尾p为空,则说明第i个元素不存在;

否则查找成功,将欲删除结点p->next赋值给q;

单链表的删除标准语句p->next = q->next;

将q结点中的数据赋值给e,作为返回;

释放q结点。

效率PK

我们最后的环节是效率PK,我们发现无论是单链表插入还是删除算法,它们其实都是由两个部分组成:第一部分就是遍历查找第i个元素,第二部分就是实现插入和删除元素。

从整个算法来说,我们很容易可以推出它们的时间复杂度都是O(n)。

再详细点分析:如果在我们不知道第i个元素的指针位置,单链表数据结构在插入和删除操作上,与线性表的顺序存储结构是没有太大优势的。

但如果,我们希望从第i个位置开始,插入连续10个元素,对于顺序存储结构意味着,每一次插入都需要移动n-i个位置,所以每次都是O(n)。

而单链表,我们只需要在第一次时,找到第i个位置的指针,此时为O(n),接下来只是简单地通过赋值移动指针而已,时间复杂度都是O(1)。

显然,对于插入或删除数据越频繁的操作,单链表的效率优势就越是明显啦~

 三:单链表的头插法和尾插法:指当链表新增一个节点是插入到每一个之前还是之后。
 头插法是插入到第一个节点(有可能是头结点,也有可能是首节点)之前,尾插法是插入到最后一个节点之后。
头插法是新增节点总是插在头部,以带头节点链表为例,链表头指针是Head,新增节点p,则:
p->next=Head->next;
Head->next=p
如果是不带头结点的链表对应:
p->next=Head;
Head=p;
而尾插法是将新增节点插在链表尾部
for(t=Head;t->next;t=t->next);                           //结束时t指向尾节点
p->next=NULL;                                                //进行插入
t->next=p;
 

转载于:https://www.cnblogs.com/wycBlog/p/7145931.html

【数据结构链表】之五单链表相关推荐

  1. python单链表实现具体例子_Python实现数据结构线性链表(单链表)算法示例

    本文实例讲述了Python实现数据结构线性链表(单链表)算法.分享给大家供大家参考,具体如下: 初学python,拿数据结构中的线性链表存储结构练练手,理论比较简单,直接上代码. #!/usr/bin ...

  2. 「数据结构 | 链表」单链表、双向链表节点操作演示动画

    目录 1. 什么是链表 2. 链表数据结构 2.1 单链表数据结构(Java) 2.2 双向链表数据结构(Java) 3. 单链表操作动画 3.1 新增节点 3.2 删除节点 4. 双向链表操作动画 ...

  3. 数据结构与算法--单链表相关面试题

    此文章仅作为自己学习过程中的记录和总结,同时会有意地去用英文来做笔记,一些术语的英译不太准确,内容如有错漏也请多指教,谢谢! 一.概述 获取单链表的有效元素个数[新浪面试题1] 获取单链表倒数第k个结 ...

  4. python数据结构基础(单链表,多链表,二叉树)

    python数据结构基础(单链表,多链表,二叉树) 数据结构指数据对象中数据元素之间的关系 Python 给我们提供了很多现成的数据结构类型,这些系统自己定义好的,不需要我们自己去定义的数据结构叫做 ...

  5. 【数据结构与算法】 01 链表 (单链表、双向链表、循环链表、块状链表、头结点、链表反转与排序、约瑟夫环问题)

    一.线性表 1.1 概念与特点 1.2 线性表的存储结构 1.3 常见操作 1.4 应用场景 二.链表 2.1 链表简介 2.2 单向链表(单链表) 2.21 基本概念 2.22 单链表基本操作 2. ...

  6. 数据结构之——《单链表》

    数据结构之--<单链表> 1.链表概念 2.链表分类 3.接口函数实现 1.链表概念 链表是一种物理存储结构上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现. ...

  7. 数据结构精讲——单链表

    新手必会数据结构精讲--单链表 链表的介绍 概念:链表是一种物理存储结构上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 . 实际中链表的结构非常多样,以下情况组合起来就 ...

  8. 数据结构5: 链表(单链表)的基本操作及C语言实现

    逻辑结构上一个挨一个的数据,在实际存储时,并没有像顺序表那样也相互紧挨着.恰恰相反,数据随机分布在内存中的各个位置,这种存储结构称为线性表的链式存储. 由于分散存储,为了能够体现出数据元素之间的逻辑关 ...

  9. 【数据结构|链表】单链表基本操作

    文章目录 一.链表 二.链表分类 三.单链表基本操作   3.1 单链表存储结构Init   3.2 遍历链表visitlist函数   3.3 求表长length   3.4 查找listfind ...

最新文章

  1. 动态执行流程分析和性能瓶颈分析的利器——gperftools的Cpu Profiler
  2. 骚操作 !IDEA 防止写代码沉迷插件 !
  3. OpenCV二值图像分析之形态学应用技巧
  4. 教你用ERD轻松修改系统登录密码
  5. switch 使用使用小技巧
  6. JZOJ 3.10 1542——跑步(树状数组+模拟+排序/归并排序)
  7. 项目上线,php的错误信息必须不让其在页面中显示给客户,
  8. Spring5参考指南:容器扩展
  9. 大数据技术周报第 006 期
  10. php urledcode_php慎用urldecode函数
  11. KEIL使用教程——KEIL常用配置技巧
  12. Java平台划分-J2EE,J2SE,J2ME
  13. 对select标签中的option默认选中后端的数据
  14. CAD分图后批量重命名图纸名称
  15. 初级对于湘源辅助cad制作城市规划图纸的认识
  16. vscode输入特殊符号
  17. Zabbix部署详解
  18. java实现日期加一天
  19. 中国建筑玻璃涂料市场深度研究分析报告
  20. Nomad 多job/group/task调度测试

热门文章

  1. android surfaceflinger研究----SurfaceFlinger loop
  2. Android 底部导航栏的简单实现-BottomNavigationView
  3. linux日志绕接,[判断题] 绕接式保安接线排按结构分为固定式和旋转式。
  4. JZOJ 5436. 【NOIP2017提高A组集训10.30】Group
  5. JZOJ 4307. 【NOIP2015模拟11.3晚】喝喝喝
  6. JZOJ 4910. 【NOIP2017模拟12.3】子串
  7. weblogic连接池不释放问题解决_数据库连接池引起的FullGC问题,看我如何一步步排查、分析、解决...
  8. python中函数的使用_python中的函数的使用示例
  9. Endnote X3 出现中文乱码
  10. matlab中find函数简单用法