【数据结构链表】之五单链表
一:链表基础
1、链表基础----------------摘录自百度百科
在创建链表之前,我们需要需要了解一下专业术语:
首节点:存放第一个有效数据的节点;
尾节点:存放最后一个有效数据的节点;
头节点:头节点的数据类型与首节点的数据类型相同,并且头节点是首节点前面的那个节点,并不存放有效数据;头节点的存在只是为了方便链表的插入和删除等操作。
头指针:指向头节点的指针;
尾指针:指向尾节点的指针;
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)。
头指针
头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。
头指针具有标识作用,所以常用头指针冠以链表的名字(指针变量的名字)。
无论链表是否为空,头指针均不为空。
头指针是链表的必要元素。
头结点
头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(但也可以用来存放链表的长度)。
有了头结点,对在第一元素结点前插入结点和删除第一结点起操作与其它结点的操作就统一了。
头结点不一定是链表的必须要素。
单链表图例:
单链表图例
空链表图例:
空链表图例
获得链表第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)。
显然,对于插入或删除数据越频繁的操作,单链表的效率优势就越是明显啦~
转载于:https://www.cnblogs.com/wycBlog/p/7145931.html
【数据结构链表】之五单链表相关推荐
- python单链表实现具体例子_Python实现数据结构线性链表(单链表)算法示例
本文实例讲述了Python实现数据结构线性链表(单链表)算法.分享给大家供大家参考,具体如下: 初学python,拿数据结构中的线性链表存储结构练练手,理论比较简单,直接上代码. #!/usr/bin ...
- 「数据结构 | 链表」单链表、双向链表节点操作演示动画
目录 1. 什么是链表 2. 链表数据结构 2.1 单链表数据结构(Java) 2.2 双向链表数据结构(Java) 3. 单链表操作动画 3.1 新增节点 3.2 删除节点 4. 双向链表操作动画 ...
- 数据结构与算法--单链表相关面试题
此文章仅作为自己学习过程中的记录和总结,同时会有意地去用英文来做笔记,一些术语的英译不太准确,内容如有错漏也请多指教,谢谢! 一.概述 获取单链表的有效元素个数[新浪面试题1] 获取单链表倒数第k个结 ...
- python数据结构基础(单链表,多链表,二叉树)
python数据结构基础(单链表,多链表,二叉树) 数据结构指数据对象中数据元素之间的关系 Python 给我们提供了很多现成的数据结构类型,这些系统自己定义好的,不需要我们自己去定义的数据结构叫做 ...
- 【数据结构与算法】 01 链表 (单链表、双向链表、循环链表、块状链表、头结点、链表反转与排序、约瑟夫环问题)
一.线性表 1.1 概念与特点 1.2 线性表的存储结构 1.3 常见操作 1.4 应用场景 二.链表 2.1 链表简介 2.2 单向链表(单链表) 2.21 基本概念 2.22 单链表基本操作 2. ...
- 数据结构之——《单链表》
数据结构之--<单链表> 1.链表概念 2.链表分类 3.接口函数实现 1.链表概念 链表是一种物理存储结构上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现. ...
- 数据结构精讲——单链表
新手必会数据结构精讲--单链表 链表的介绍 概念:链表是一种物理存储结构上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 . 实际中链表的结构非常多样,以下情况组合起来就 ...
- 数据结构5: 链表(单链表)的基本操作及C语言实现
逻辑结构上一个挨一个的数据,在实际存储时,并没有像顺序表那样也相互紧挨着.恰恰相反,数据随机分布在内存中的各个位置,这种存储结构称为线性表的链式存储. 由于分散存储,为了能够体现出数据元素之间的逻辑关 ...
- 【数据结构|链表】单链表基本操作
文章目录 一.链表 二.链表分类 三.单链表基本操作 3.1 单链表存储结构Init 3.2 遍历链表visitlist函数 3.3 求表长length 3.4 查找listfind ...
最新文章
- 动态执行流程分析和性能瓶颈分析的利器——gperftools的Cpu Profiler
- 骚操作 !IDEA 防止写代码沉迷插件 !
- OpenCV二值图像分析之形态学应用技巧
- 教你用ERD轻松修改系统登录密码
- switch 使用使用小技巧
- JZOJ 3.10 1542——跑步(树状数组+模拟+排序/归并排序)
- 项目上线,php的错误信息必须不让其在页面中显示给客户,
- Spring5参考指南:容器扩展
- 大数据技术周报第 006 期
- php urledcode_php慎用urldecode函数
- KEIL使用教程——KEIL常用配置技巧
- Java平台划分-J2EE,J2SE,J2ME
- 对select标签中的option默认选中后端的数据
- CAD分图后批量重命名图纸名称
- 初级对于湘源辅助cad制作城市规划图纸的认识
- vscode输入特殊符号
- Zabbix部署详解
- java实现日期加一天
- 中国建筑玻璃涂料市场深度研究分析报告
- Nomad 多job/group/task调度测试
热门文章
- android surfaceflinger研究----SurfaceFlinger loop
- Android 底部导航栏的简单实现-BottomNavigationView
- linux日志绕接,[判断题] 绕接式保安接线排按结构分为固定式和旋转式。
- JZOJ 5436. 【NOIP2017提高A组集训10.30】Group
- JZOJ 4307. 【NOIP2015模拟11.3晚】喝喝喝
- JZOJ 4910. 【NOIP2017模拟12.3】子串
- weblogic连接池不释放问题解决_数据库连接池引起的FullGC问题,看我如何一步步排查、分析、解决...
- python中函数的使用_python中的函数的使用示例
- Endnote X3 出现中文乱码
- matlab中find函数简单用法