《算法图解》读后感以及学习总结_数据结构和大O表示法了解入门

作为一个非软件专业毕业的人,算法向来是个苦手。
尽管我觉得广大大学生都是宿舍外卖伴随四年,大概率大家都是菜逼,对于大厂的算法题目考试异常头疼,打开leetcode写是能写,但是对完答案就觉得自己是个傻逼。
然后就两眼一摸瞎,你说去抄答案吧,是可以抄,但是抄了有啥用呢?下一个算法题还是两眼一抹黑,只能勉强实现,压根考虑不到性能,或者说无从考虑,就感觉自己啥都不会。

但是看了《算法图解》这本书后,那就不一样了。
我就感觉,虽然我还是啥都不会,但我大受震撼。

言归正传,做一下读书总结,如有理解不妥的地方,还望大佬指正。
首先,过去我对于数据结构是一种模糊的认知,不能清楚的区分不同数据结构的不同,只觉得是用来存放不同类型的数据的。(我学的java,但是书里用的python,不打紧,看懂逻辑就行)
但其实我看完之后,我觉得除了int,char,boolean,double这些宏观上用来存储不同类型,不同大小的数据的东西之外,剩下的就是用来提升性能而存在的数据结构了。
而这些和性能相关的数据结构,其实归根到底是数组和链表这两种——在内存中的不同存储方式(直接一块一块放的另算)。
●数组:(知道是啥,就不用看了)
数组在内存中是连着存的,你要查找数据,只需要找到第一个数据,就可以按照+n的方式找到第n-1个数据,因此查的快。
但由于要连着,所以每次增加都有可能后面没位置了,要重新在内存里找位置,所以增比较慢。
删的时候,如果删中间的,后面的都要动,所以删也慢。
改的话本质上还是增删查。
●链表:(知道是啥,就不用看了)
在内存中随便存着,但是单位数据内会存储下一个链表的位置,使得数据得以关联起来。这样的话,即使你只知道一个起始的位置,你也可以找到每一个数据的位置,就是慢,因为它要一个一个的查过去。
但是增删的时候,由于每个数据之间最多就两个数据关联,所以,速度就快。

当我知道这两个东西的时候,其实感觉也就那样,毕竟我原本也知道,只是没有它书里图片理解的那么深刻。(图片,或者说更贴近现实的学习资料果然才是人类完整的学习媒介)
继续往后看,我就发现不一样了。
仔细思考,我自己是不是还学了一大堆比如二叉树,红黑树,哈希集合的东西,这些都是数据结构嘛~~
但是其实它们本质都是数组和链表嘛~
双向链表就是链表信息里除了存下一个以外再存个上一个嘛。(当然也可以用数组存,只不过必须是完全二叉树才能在内存中转化成数组来存储,会浪费一些内存空间,即浪费完全二叉树用来补全空节点的空数据部分所占用的空间)
二叉树极端条件下(只往一边生长)就是一个链表,只不过相当于双向链表变成了左右链表。
那平衡二叉树就是加判断条件的二叉树嘛。
红黑树就是放弃了追求完全平衡,引入红黑概念的巧劲的平衡二叉树,或者说定制规则的平衡二叉树嘛。

好家伙,原本我记忆数据结构都是一个个记忆的,现在嘛~直接继承两个父类,然后一路安装发展史继承,或者安装定制化需求分支,
比如:数据库用B树是因为,要减少IO次数,即降低树的高度,在一层里多分出一层,变成三叉树。而B+树就是在三叉树中间不存放数据,只存放一个标机数据用的值(哈希值?),然后在数据的结尾再额外添加一层,添加一层数组,通过前面存放的值去查这个结尾的数组,并再次回到索引表中,或者主键表,或者原表中去拿出语句所需数据。
好家伙,顺路连数据库索引结构,查询方式都理顺了,只要再加上一个MVCC多版本控制和事务隔离级别,数据库就也了然了。。。
这就是我大受震撼的原因。
那我是不是分析算法的时候应该考虑它要怎么存比较好?那么leetcode经典第一题好像就有思路了。

什么是散列函数?,我反正就当时hash算法,什么事hashMap,什么是键值对存储?
就通过一个万能的hash算法,把一个字符串也好乱七八糟的东西也好,总之变成一个固定的数组,即,map的value这个“值”通过这种“hash算法”后永远都是得到一个相同的“数字”,然后就可以把一个和这个“值”(value)对应的东西放到这个数组的“数字”(key)的一个特定的格子里去。
所以键值对的集合存储,本质就是数组的存储。(其实硬要用链表存也可以,但就属于南辕北辙了)
而hash桶这个东西,就是当有很多个键值对要存了,数组里每个值存的有点多了,于是乎就在每个数组格子上开始接链表,可以想象成一个电插座上连着很多插座,这些插座又再连着一个插座。(或者想象成一条章鱼,每个章鱼有很多条触须,而每条触须上又有很多顺序排列的吸盘节点)
然后呢,如果这个数组额外格子连着的链表数量长了,一般是比8多,就会把链表部分变成叉树。如果数组本身被塞满了,那就把数组的长度扩充一倍。

但是这样其实还是不能搞算法,因为我还不能衡量到底那个算法更好,于是乎就得引入大O表示法,顺带的学会了分而治之的递归思想。
我思考算法速度,大多时候都是一个循环一个循环的想,最后挠掉了几根头发之后终于想出了来了,选出了更快的那个,然后发现慢了。
大O表示法,其实就是用O(n)这种写法表示查找某一批n个数据时的要进行多少步运算 。
但是这个运算其实不是特指一步,或者两步,而是指“一步”。
有点绕。
这里需要引入递归思想,不是简单的数学递归,而是一种思想。
听不懂,哈哈,我以前也不懂,以为递归就是递归。
试着把一个1680640的长方形全部均分成长宽一样的正方形,最大能分出多大边长的正方形呢?
书里翻译的名词是分而治之,我反正也不太准确,但我喜欢叫它递归思想,书籍前后也确实在讲这个东西。
当你明白什么是递归思想后,你就能很快的分出什么是“一步”运算,即,把那个正方形涂黑就算一步。
当你知道一步是多少了,你自然就能知道总共要走多少步,即,有多少个正方形,即,算法的深度是多少。
当你知道算法深度是多少之后,再回过头去看一眼每“一步”你要处理多少数据。即,书里说的“基线”。基线这一步的算处理多少个数据。
好,那么当知道了“一步”,又知道了“一步”要多久,又知道了要走“几步”,算法到底怎么样是不是就能表示出来了?比如O(n
n)比如O(n*logn)
要注意的是,考虑算法的时候不太需要考虑常数,即,是1还是2,还是2/3,都不打紧,统统当成1,,写成O(n)就完事了。
注意这里不是完全因为不考虑常数这种细微影响,而是没法考虑,或者难以考虑,考虑起来很浪费时间,也缺少意义。很多时候当元素为n时,你要统计你的每一个“一步”是不是“步幅”都大小一致,而不是“步幅”具体有多大。
引用书中描述快速排序法的话:“只要你每次都随机地选择一个数组元素作为基础值,快速排序的平均运行时间就将为O(nlogn)”
尽管我觉得这样不严谨,但显然你要研究算法,整天只想着优化常数级的东西,不管效果如何,头发就难以茂密了。

空间复杂度和时间复杂度都可以用O表示法表示

由分而治之还可以入门归并排序,并写个多线程归并,由此入门并发并行处理的多线程问题和锁的概念。还可以想想递归和迭代的区别,顺路发现一下java为什么一些方向不够吊。

到这里为止,我就掌握了算法的砖(数据结构),还有怎么方便的衡量算法优劣的尺子(递归思想下的大O表示法)

尽管我还啥都不会,但是我大受震撼。

书中后面还较为详细的讲了广度优先搜索,迪克拉斯特算法,贪婪算法,动态规划,其他一些东西就只是单纯的一笔带过了。
简单总结一下就是:
贪婪算法是通过无数局部最优解来推导出一个近似最优解,来解决几乎不可能硬解出完美解的那一类问题(NP问题)的一种思考方式。(广度优先解决不加权的选择问题,迪克拉斯解决加正数权的选择问题,贝尔曼福特则彻底解决加权选择问题,但其实贝尔曼福特算法就是用了n次的迪克拉斯特算法)
动态规划则是通过网格来将大问题分为小问题,然后递归后归并回来的一种思考方式。

可能以后还会再总结更多,但先这样了吧

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值