漫画算法-小灰的算法之旅
魏梦舒(@程序员小灰)著
小灰用漫画(可爱的手绘小仓鼠)的形式,给算法这颗“炮弹”包上了“糖衣”,让算法的为力潜藏于内,外表不再吓人,变得萌萌哒,Q弹可爱。
本书通过主人公小灰,用漫画的形式讲述了算法与数据结构的基础知识、复杂多变的算法面试及算法的实际应用。
“学习算法,我们不需要死记硬背那些冗长复杂的背景知识、底层原理、指令语法......需要做的是领悟算法思想、理解算法对内存空间和性能的影响,以及开动脑筋去寻求解决问题的最佳方案。相比编程领域的其他技术,算法更纯粹,更接近数学,也更具有趣味性。” -- 作者说
算法
algorithm。始于计算出1+2+3+4...+10000的结果。首先把从1到10000这10000个数字两两分组相加,如下:
1 + 10000 = 10001
2 + 9999 = 10001
3 + 9998 = 10001
......
一共有多少组这样结果相同的和呢?有10000/2即5000组,即结果:(1 + 10000) * 10000 / 2 = 50005000
。
在数学上称这种等差数列求和的方法为:高斯算法。
在数学领域:算法是用于解决一类问题的公式和思想。
在计算机领域:算法本质是一序列程序指令,用于解决特定的运算和逻辑问题。衡量算法好坏的重要标准有两个:
应用:运算、查找、排序、寻找最优路线、面试等。
数据结构
数据结构是算法的基石。如果把算法比喻成美丽灵动的舞者,那么数据结构就是舞者脚下广阔而坚实的舞台。
data structure,是数据的组织、管理和存储格式,其使用的目的是为了高效地访问和修改数据。
数据结构组成方式:
时间复杂度
是对一个算法运行时间长短的量度,用大O表示,记作T(n) = O(f(n))
直白地讲,时间复杂度就是把程序的相对执行时间函数T(n)简化为一个数量级,这个数量级可以是n、n2、n3等。
推导出时间复杂度,有如下几个原则:
比如:
执行次数是T(n) = 3n,最高阶项为3n,省去系数3,转化的时间复杂度为T(n) = O(n)
执行次数是T(n) = 5logn,最高阶项为5logn,省去系数5,转化的时间复杂度为T(n) = O(logn)。5logn,数学中的对数。
执行次数是T(n) = 2,只有常数量级,转化的时间复杂度为T(n)=O(1)
执行次数是T(n) = 0.5n2 + 0.5n,最高阶项为0.5n2,省去系数0.5,转化的时间复杂度为T(n) = O(n2)
谁更节省时间呢?结论如下:
O(1) < O(logn) < O(n) < O(n2)
空间复杂度
space complexity。是对一个算法在运行过程中临时占用存储空间大小的度量,用大O表示,记作S(n) = O(f(n))
数组
有限个,在内存中顺序存储。
内存是由一个个连续的内存单元组成的,每一个内存单元都有自己的地址。
数组中的每一个元素,都存储在小小的内存单元中,并且元素直接紧密排列,既不能打乱元素的存储顺序,也不能跳过某个存储单元进行存储。
操作:
插入、删除的时间复杂度都是O(n)
优势:非常高效的访问能力,只要给出下标,就能找到对应元素。
劣势:插入、删除效率低下。数组元素连续紧密地存储在内存中,插入、删除元素都会导致大量元素被迫移动,影响效率。
数组适合读操作多、写操作少的场景。链表和数组正好相反。
链表
一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。
单向链表的每一个节点包含两部分,存放数据的变量data,指向下一个节点的指针next。
链表的第一个节点被称为头结点,最后一个节点被称为尾节点。尾结点的下一个节点指向空。
双向链表,每一个节点除了拥有 data 和 next,还拥有指向前置节点的 prev 指针。
数组在内存中的存储方式是顺序存储,链表 在内存中的存储顺序是随机存储。
数组在内存中占用了连续完整的存储空间,而链表采用了见缝插针的方式,链表的没一个节点分布在内存的不同位置,依靠next指针关联起来。这样可以灵活有效地利用零散的碎片空间。
链表操作:
;链表的插入和删除的时间复杂度:如果不考虑插入、删除操作之前查找元素的过程,只考虑纯粹的插入、删除操作,时间复杂度为O(1)
数组 vs 链表:数组能够利用下标快速定位元素,对于读操作多、写操作少的场景非常使用。而链表的有事在于能灵活的进行插入和删除操作,适用于读操作少、写操作多的场景。
栈和队列
数据存储的物理结构和逻辑结构:
栈:stack。一种线性数据结构,栈中的元素只能先进后出(FILO,First In Last Out)。最早进入的元素存放的位置叫作栈底,最后进入的元素存放的位置叫作栈顶。用数组、链表均可以实现。
栈的基本操作:
入栈、出栈的时间复杂度:O(1),因为只会影响到最后一个元素。
队列:queue。一种线性数据结构,队列中的元素只能先进先出(FIFO,First In First Out)。队列的出口端叫作队头(front),队列的入口端叫作队尾(rear)。用数组、链表均可以实现。
队列的基本操作:
额外:循环队列、双端队列、优先队列。
散列表
散列表:也称哈希表,hash table。是存储 key-value 映射的集合,时间复杂度接近于O(1)。本质上也是一个数组。
哈希函数:通过某种方式,把 key 和数组下标进行转换的函数。
散列表的读写操作:
树
tree,是n个节点的有限集。有如下特点:
树的最大层级树,被称为树的高度或深度。
相关节点
二叉树
树的一种特殊形式。树的每个节点最多有2个孩子节点。
二叉树的两个孩子节点,一个被称为左孩子,一个被称为右节点。
二叉树有两种特殊形式:满二叉树、完全二叉树。
满二叉树:一个二叉树的所有非叶子节点都存在左右孩子,并且所有叶子节点都在同一层接上。简言之,满二叉树的每一个分支都是满的。
完全二叉树:对一个有n个节点的二叉树,按层级顺序编号,则所有节点的编号为从1到n。如果这个树所有节点和同样深度的满二叉树的编号为从1到n的节点位置相同,则这个二叉树为满二叉树。
一棵树,若为满二叉树,那么一定是完全二叉树。反之,不一定。
在内存中存储:
二叉树的应用:查找操作、维持相对顺序。
二叉树的遍历:
从节点之间位置关系的角度:
从更宏观的角度:
二叉堆:本质上是一种完全二叉树。
二叉堆的根节点,叫作堆顶。最大堆的堆顶是整个堆中最大元素,最小堆的堆顶是整个堆中最小元素。
优先队列:基于二叉堆实现:
观后感:回想起了很多大学学习时的场景与概念(专业:软件工程)
原文:https://www.cnblogs.com/EnSnail/p/11037209.html