数据结构与算法基础(一)

1,461 阅读5分钟

我们学习数据结构与算法的目的是建立时间复杂度 空间复杂度意识,写出高质量的代码,能够设计基础架构,提升编程技能,训练逻辑思维。

概念

数据结构就是指一组数据的存储结构,算法就是操作数据的一组方法。 数据结构是为算法服务的,算法是要作用在特定的数据结构之上。

常用的数据结构与算法

数据结构:数组 链表 栈 队列 散列表 二叉树 堆 跳表 图 trie 树

算法: 递归 排序 二分查找 搜索 哈希算法 贪心算法 分治算法 回溯算法 动态规划 字符串匹配算法

复杂度

复杂度分为时间复杂度和空间复杂度,实际就是衡量代码怎么运行的更快,怎么样运行的更省的问题,通俗点说就是效率和存储空间。

事后统计法

事后统计法就是说把代码实实在在的跑一遍,通过统计,监控,就能得到算法执行的时间和占用的内存大小。但是这种方法对有很大的局限性。

  • 测试结果非常依赖环境
  • 测试结果受数据规模的影响很大

为了避免这种局限性我们就需要大o复杂度表示法

时间复杂度

大o时间复杂度实际上并不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势,也叫作渐进时间复杂度,简称时间复杂度。

如何分析一段代码的时间复杂度:

  • 只关注循环执行次数最多的一段代码
  • 总复杂度等于量级最大的那段代码的复杂度
  • 嵌套代码的复杂度等于嵌套内外代码复杂度的乘积

几种常见的时间复杂度 O(1) 常量级时间复杂度一种表示方式,并不是指只执行一行代码。只要代码的复杂度不随着n的增大而增长,这们的代码的时间复杂度我们都记作O(1)。

O(logn) 对数阶时间复杂度

O(m+n) O(m*n) 代码的复杂度是由两个数据的规模来决定

空间复杂度

渐进空间复杂度表示的就是算法的存储空间与数据规模之间的增长关系。

空间复杂度一般比较简单,大多都是O(n).

最好 最坏情况时间复杂度

最好情况时间复杂度(best case time complexity):最好情况时间复杂度就是,在最理想的情况下,执行这段代码的时间复杂度。

最坏情况时间复杂度(worst case time complexity):最坏情况时间复杂度就是在最糟糕的情况下,执行这段代码的时间复杂度。

平均情况时间复杂度 加权平均时间复杂度 或者 期望时间复杂度

均摊时间复杂度

为什么很多语言中数组都是从0开始编号

数组是一种线性表数据结构,它用一线连续的内存空间,来存储一组具有相同类型的数据。(线性表就是数据排成像一条线一样的结构,每个元素只有前和后两个方向,例如 链表、队列、栈也是线性表结构。非线性表,比如二叉树、堆、图等,之所叫非线性,是因为,数据之前并不是简单的前后关系)。 数组适合随机访问,但是对于插入和删除很是低效。 对于低效的操作我们可以采用JVM思想(标记清除垃圾回收算法),每次的删除操作并不是真正的搬移数据,只是记录数据已经被删除,当数组没有更多空间存储数据时,我们再触发执行一次真正的删除操作,这样就大大减少了删除操作导致的数据搬移。

如果数据从1开始编号,每次随机访问数组元素都多了一次减法运算,对于cpu来说,就是多了一次减法指令。所以为了减少一次减法操作,数组选择了从0开始编写,而不是从1开始。

LRU缓存淘汰算法

缓存是一种提高数据读取性能的技术,在硬件设计、软件开发中都有着非常广泛的应用,比如cpu缓存、数据库缓存、浏览器缓存等。

缓存的大小有限,当缓存被用满时,哪些数据应该被清理出去,就是需要缓存淘汰策略做决定。

  • 先进先出策略 FIFO(first in first out)
  • 最少使用策略 LFU (least frequently used)
  • 最近最少使用策略 LRU (least recently used)

数组需要一块连续的内存空间,对内存的要求比较高,而链表的恰恰相反,它并不需要一块连续的内存空间,它通过“指针”将一组零散的内存块串联起来使用。

三种最常见的链表结构:

  • 单链表
  • 双向链表
  • 循环链表

链表相对数组来说,他的插入和删除一个数据是非常快速的。 但是随机访问第K个元素,就没有数组那么高效了。

用空间换时间 当内存空间充足的时候,我们就可以选择空间复杂度相对较高、但是境复杂度相对很低的算法或者数据结构。相反,如果内存比较紧张,比如代码跑在手机或者单片机上,这时候,就要反过来用时间换空间的设计思路。