版权声明:本文出自汪磊的博客,未经作者允许禁止转载。

一、前言

项目进入收尾阶段,忙忙碌碌将近一个多月吧,还好,不算太难,就是麻烦点。

数据结构与算法这个系列早就想写了,一是梳理总结,顺便逼迫自己把一些模模糊糊的概念弄明白,最重要的我觉得数据结构与算法平时我们总是接触,什么ArrayList,LinkedList,HashMap...这些我们总是接触,但是在使用的时候有多少是会考虑一下性能方面问题,是不是你也不管三七二十一上来就是ArrayList,往里面扔数据呗,反正运行一般没问题,就像写文章一逗到底,一个字:爽。

此外,对于个人长远发展也十分重要的,基础扎实,不惧任何所谓的新技术。好了,本篇比较简单,介绍一下时间与空间复杂度概念,以及基本数据结构:数组,链表以及哈希表。

二、时间复杂度与空间复杂度概念

时间复杂度

在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间量度。记作:T(n)=O(f(n))。它表示随问题n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐进时间复杂度,简称为时间复杂度。其中,f(n)是问题规模n的某个函数。

这样用大写O()来体现算法时间复杂度的记法,我们称之为大0记法。

以上是官方说法,我懂你现在的心情,这尼玛什么玩意,我用人话翻译一下哈:在分析算法时间复杂度时我们要找到一个函数f(n),使得当n无限大时能近似表示函数语句的执行次数,此时函数f(n)就可以近似表示此算法的时间复杂度。

咦?时间哪去了?怎么来了个算法的执行次数?其实真正的算法执行时间怎么可能用函数表示出来,只有跑起来才能知道算法执行时间,但是算法的执行次数是可以知道的,然而至于算法执行时间无非就是执行次数*每次执行时间,每次执行时间近似差不多的,所以也就可以用执行次数来大体估算算法的执行时间了。那怎么计算呢?继续看。

计算时间复杂度

首先算出算法执行次数的函数f(n),然后进行下面操作:

1、用常数1取代运行时间中的所有加法常数。

2、在修改后的运行次数函数中,只保留最高阶项。

3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。

我知道上面三条你又觉得蛋疼了,我们以实际例子为例讲解一下:

时间复杂度计算例子:

1 int sum = 0, n = 100;2 sum = (1 + n) * n / 2;3 printf("%d",sum);

三行代码总共执行了三次,所以此算法执行次数函数:

f(n) = 3.执行上面操作:

1、用常数1取代运行时间中的所有加法常数,f(n) = 3函数很简单,就一个常数3,被1取代,所以变为:

f(n) = 1

2、在修改后的运行次数函数中,只保留最高阶项。

3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。

至于2,3条此函数根本就没有最高阶项,所以也就无从谈起了,所以最终时间复杂度函数表示为O(1).

上面例子很简单,我们再看一个复杂点的:

1 inti, j;2

3 for(i = 0; i < n; i++){4

5 for(j = i; j < n; j++){ //j = i,不是06

7 }8 }

先算一下算法执行次数:

当i = 0时,内层循环执行n次,当i = 1时,内层循环执行n-1次,当i = 2时,内层循环执行n-2次,当i = n-1时,内层循环执行1次,当i = n时,内层循环执行0次,所以此算法执行函数为:

f(n) = 0 +1+2+3+...+(n-1)+n = n(n+1)/2 = n2/2 + n/2

接下来执行上面操作,取近似表示法:

1、用常数1取代运行时间中的所有加法常数

次函数没有常数项所以此条不用操作了

2、在修改后的运行次数函数中,只保留最高阶项

次函数最高阶项为n2/2,所以简化后函数变为f(n) = n2/2

3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶

最高阶项系数为1/2,不为1,去除,所以最终f(n) = n2

最终此算法时间复杂度表示为O(n2)

好了,关于时间复杂度想说的就这些吧,关键理解时间复杂度表示的意义以及一些常见算法时间复杂度的计算。

空间复杂度

我们在写代码时,完全可以用空间来换取时间,比如说,要判断某某年是不是闰年,你可能会花一点心思写了一个算法,而且由于是一个算法,也就意味着,每次给一个年份,都是要通过计算得到是否是闰年的结果。 还有另一个办法就是,事先建立一个有2050个元素的数组(年数略比现实多一点),然后把所有的年份按下标的数字对应,如果是闰年,此数组项的值就是1,如果不是值为0。这样,所谓的判断某一年是否是闰年,就变成了查找这个数组的某一项的值是多少的问题。此时,我们的运算是最小化了,但是硬盘上或者内存中需要存储这2050个0和1。这是通过一笔空间上的开销来换取计算时间的小技巧。到底哪一个好,其实要看你用在什么地方。

算法的空间复杂度通过计算算法所需的存储空间实现,算法空间复杂度的计算公式记作:S(n)= O(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数。

一般情况下,一个程序在机器上执行时,除了需要存储程序本身的指令、常数、变量和输入数据外,还需要存储对数据操作的存储单元,若输入数据所占空间只取决于问题本身,和算法无关,这样只需要分析该算法在实现时所需的辅助单元即可。若算法执行时所需的辅助空间相对于输入数据量而言是个常数,则称此算法为原地工作,空间复杂度为0(1)。

通常, 我们都使用"时间复杂度"来指运行时间的需求,使用"空间复杂度"指空间需求。当不用限定词地使用"复杂度'时,通常都是指时间复杂度。

最后附上常见排序算法时间复杂度与空间复杂度:

好了,关于时间与空间复杂度就聊到这里。

三、数据结构之数组

数组你我很熟悉了,这里只是总结一下。

数组内存中存储方式如下:

数组在内存中是一块连续的存储单元存储起来的,声明数组的时候我们必须声明其长度,这样才会为我们声明一个连续的存储区域。

这种存储方式造成我们想要往数组中存储一个数据时那么其后面各个元素都要往后移动,同样的,删除数据后面的数据都要往前移动。

但是同样也带来好处,我们要想获取数组中第i个元素,直接通过角标获取即可,同理修改也是。

数组获取第i个数据的算法时间复杂度为O(1),直接通过角标获取即可,执行一次。

数组查找其内是否包含某一元素表现不好,因为只有挨个比较才可以,试想一下数组特别大的情况下,挨个比较一下还是挺费劲的,此算法时间复杂度为O(n)。

简单总结:数组获取某一数据很简单通过角标i直接获取即可,但是增删比较低效,在内存中用一块连续的存储区域来存储,查找数组中是否包含某一元素比较低效。

四、数据结构之链表

与数组不同,链表不用非要一块连续的存储区域,链表是一种离散存储结构,数据之间通过指针链接,每个数据元素包含数据域与指针域,数据域存储对应数据即可,而指针域则指向下一个数据元素(对于单项链表来说),针对指针域还可以分为单向链表,双向链表,循环链表。

单项链表存储模型:

对于双向链表就是一个数据元素包含三部分:数据域,前向指针,后向指针。前向指针指向前一个数据元素,后向指针就像单向链表一样指向后一个数据元素:

循环链表就是在双向链表基础上将首尾数据元素通过指针连接起来:

三种链表大体如上,顺带秀了一下我强大的画图能力,哈哈...

由于链表的离散存储方式,使其与数组有很大区别;

链表要是想获取某一个元素那可费劲了,需要我们从第一个元素开始挨个遍历查找,修改也同理,因为当前元素地址存储在上一个元素的后向指针里,所以只能从头挨个遍历查找,所以相对于数组链表存储方式获取元素,修改元素效率低。

链表对于插入,删除一个数据元素效率比较高,不像数组那样影响后续所以元素,只需操作前后元素的指针即可。

链表不要求必须有一块连续的存储区域,所以不会造成内存碎片化。

链表中对于查找某一元素是否在链表中与数组一样也需要挨个比较每个元素,所以也比较低效,时间复杂度为O(n)。

与数组相比每一个数据项占用内存会变多,因为要多存储指针域。

简单总结:链表增删效率高,查找效率低。每一个数据项与数组相比更耗内存。不需要整块内存块,不会造成碎片化。

五、数据结构之哈希表

上面分析可以看到无论数组还是链表对于查找一个数据是否存在数组中或者链表中效率都比较低,都需要挨个比较,那有没有一种办法可以消除一部分数据的对比呢?先人们想来想去,哈希表横空出世了。

哈希表就是一种以键-值(key-indexed) 存储数据的结构,我们只要输入待查找的值即key,即可查找到其对应的值。

存储时key类型是不确定的,可能是int,可能是String,也可能是其他任意对象。Hash函数的作用就是把这些对象通过合理的方式转为int类型,从而完成数据的存储。

更形象类比就是字典了(几乎所有讲到哈希表的博文都用此类比),小时候我们查字典会先根据拼音去查对应页数,然后再去对应页数去查询要找的字,哈希表也是同样道理,此处不过多表述。

哈希碰撞问题

提到哈希表,必然提一下哈希碰撞问题,哈希碰撞两个不同的原始值在经过哈希运算后得到同样的结果,这样就是哈希碰撞。这里就有疑问了?为什么两个不同的值经过哈希运算后还会得到相同的值呢?简单来说哈希算法并不完美,是会出现这种情况的,此处就不必深究了,如果你时间充足,那就去深入研究一下哈希算法吧。

哈希碰撞解决办法有开放定址法,链地址法等等,我们需要额外关注链地址法。

链地址法其实就是HashMap中用的策略,数组+链表的实现形式。

原理是在HashMap中同样哈希值的位置以一串链表存储起来数据,把多个原始值不同而哈希结果相同的数据以链表存储起来,类似如下:

哈希表优缺点:

哈希表能同时兼备数组和链表的优点,它能在插入和查找时都具备良好的性能。

设计不好的哈希表,有可能会出现较多的哈希碰撞,导致链表过长,从而哈希表会更像一个链表。

还有当数据量很大时,为防止单个链表过长,就需要对数组进行扩容,对性能的影响也很严重。

好了,本篇到这里就该结束了,整篇没有什么源码方面的分析,放心后续会有大量源码解析的,先把一些基础概念搞清楚吧,希望对你有用。

声明:文章将会陆续搬迁到个人公众号,以后文章也会第一时间发布到个人公众号,及时获取文章内容请关注公众号

android 数据结构详情,Android数据结构与算法(一):基础简介相关推荐

  1. android webview详情,Android中的WebView详细介绍

    Android中WebView的详细解释: 1. 概念: WebView(网络视图)能加载显示网页,可以将其视为一个浏览器.它使用了WebKit渲染引擎加载显示网页. 2. 使用方法: (1).实例化 ...

  2. android 数据结构详情,Android原生的数据结构

    HashMap HashMap内部是使用一个默认容量为16的数组来存储数据的. 数组中每一个元素却又是一个链表的头结点. HashMap内部存储结构是使用哈希表的拉链结构(数组+链表) 且每一个结点都 ...

  3. android 电量详情,Android应用开发之Android 8.0 电池-)耗电详情获取方法

    本文将带你了解Android应用开发之Android 8.0 电池-)耗电详情获取方法,希望本文对大家学Android有所帮助. Android 8.0 电池-)耗电详情获取方法 主要介绍UI位置和基 ...

  4. 优秀博客链接(linux c/c++ java go php android ios 前端 j2ee windows linux 算法 ACM 深度/机器学习 AI opencv nlp)

    pudn 阿甘兄 前端 服务端 底层 移动端 大数据 云计算 AI 培训机构的课程差不多就这一套了 大数据 AI NLP 高等数学 LeetCode.<数据结构与算法之美>学习笔记.AI ...

  5. Java数据结构(1.1):数据结构入门+线性表、算法时间复杂度与空间复杂度、线性表、顺序表、单双链表实现、Java线性表、栈、队列、Java栈与队列。

    数据结构与算法入门 问题1:为什么要学习数据结构          如果说学习语文的最终目的是写小说的话,那么能不能在识字.组词.造句后就直接写小说了,肯定是不行的, 中间还有一个必经的阶段:就是写作 ...

  6. Android程序员必备!海量算法高频面试题精编解析,真香

    前言 这里是我整理的2019年至2021年期间通过各个渠道花时间专门整理的面试题,其中面试重点和难点都有详细解析,重点讲的是Android各方面的专题讲解包括Java小部分的技术讲解.这些题目有点技术 ...

  7. 优秀博客链接(linux c/c++ java go php android ios 前端 j2ee windows linux 算法 ACM AI 深度/机器学习 opencv nlp)

    pudn 阿甘兄 前端 服务端 底层 移动端 大数据 云计算 AI 培训机构的课程差不多就这一套了 LeetCode.<数据结构与算法之美>学习笔记.AI 前端资料学习,vue.js re ...

  8. android scrollview 布局,Android scrollview实现底部继续拖动查看图文详情

    本文实例为大家分享了Android实现底部拖动查看图文详情的具体代码,供大家参考,具体内容如下 一.效果图 二.实现步骤 1.xml布局的实现/p> android:id="@+id/ ...

  9. 【Android 内存优化】垃圾回收算法 ( 分代收集算法 | Serial 收集器 | ParNew 收集器 | Parallel Scavenge 收集器 | CMS 并发标记清除收集器 )

    文章目录 一. 分代收集算法 二. 垃圾回收器 / 收集器 ( GC ) 三. 串行收集器 ( Serial ) 四. ParNew 收集器 五. Parallel Scavenge 收集器 六. C ...

  10. 大话数据结构与算法:基础篇

    1.数据结构的重要性 数据结构是计算机软件相关专业的基础课程,几乎可以说,要想从事编程工作,无论是否是科班出身(比如我,标准的非科班人员,我是学医的,哈哈)都不可以绕过数据结构与算法这部分知识. 数据 ...

最新文章

  1. Redis亿级数据过滤和布隆过滤器
  2. python异常值处理实例_Python异常值处理与检测
  3. react native 从头开始
  4. vue 父组件与子组件之间的传值(主动传值)
  5. 【今日CS 视觉论文速览】Thu, 13 Dec 2018
  6. feign使用_Feign:介绍与使用
  7. 51cto案例精解第一章PPT
  8. c语言文件io的fork,15. C语言的fork
  9. 华为存储认证怎么样?华为存储认证题库哪里有?
  10. IBM DB2各版本下载地址
  11. TLE(两行轨道数据)卫星星历中时间转换方法(C#)
  12. 计算机控制面板设置命令,进入开始---设置--控制面板--声音和音频设备命令
  13. big mac sur 免驱显卡_macOS Big Sur 系统原生显卡驱动信息表
  14. wannacry 蠕虫勒索软件“永恒之蓝”席卷全球100多个国家,已经感染了勒索病毒“永恒之蓝”的主机该怎么处理?
  15. ubuntu php7 pdo,记录捣鼓ubuntu下PHP7.1下安装sqlserver拓展
  16. 如何设置payjs的微信jsapi支付目录
  17. 测试功能点----方法
  18. 异常:java.lang.LinkageError: loader constraint violation: when resolving interface method “javax.servl
  19. asp.net监听输入框值的即时变化onpropertychange、oninput
  20. VLAN 、PVLAN

热门文章

  1. 吉他入门教程之吉他音阶训练——认识音阶
  2. Microsoft PowerPoint无法执行语言识别
  3. 高版本SDK编译生成的apk放入低版本android源码中集成编译
  4. gitlab内网部署clone push速度快,网页反应慢
  5. 由对称性知定点一定在x轴上_圆锥曲线中的定点定值问题的四种模型.doc
  6. 用C语言读把SGY地震数据读成txt
  7. elementUI Cascader 级联选择器 拼音模糊搜索
  8. 喜马拉雅FM下载的音频转换为正常文件的JAVA实现
  9. impala hive随机抽样方法
  10. 快速批量重命名文件(夹)