数据结构与算法之美-学习笔记

    接上篇文章,在我意识到数据结构与算法的重要性时,正好在群里有人分享了极客时间的数据结构与算法之美的课程,从入门篇、基础篇、高级篇到实战篇,由浅入深的讲述常用的数据结构与算法,特别是在留言区作者的留言"迈不过去你找我退钱",我就喜欢这种有自信的人,当然不是完全指望他人帮自己把算法捡起来,
既然来了,就要全身心的投入,在此立个flag,通过这个阶段的学习,理解常用的算法与数据结构,掌握复杂度的分析,将提高代码执行效率思想融入日常开发中。

第1章 为什么要学习数据结构与算法
目的:我们学习数据结构与算法,并不是为了死记硬背几个知识点。我们的目的是建立时间复杂度、空间复杂度意识,写出高质量的代码,能够设计基础架构,提升编程技能,训练逻辑思维,积攒人生经验,以此获得工作回报,实现个人价值。
第二章 如何抓住重点,系统高效的学习数据结构与算法
定义:从广义上讲,数据结构就是指一组数据的存储结构,算法就是操作数据的一组方法。
两者关系:数据结构与算法是相辅相成的,数据结构是为算法服务的,算法要作用在特定的数据结构之上。
                    数据结构是静态的,它只是组织数据的一种方式。如果不在它的基础上操作、构建算法,孤立存在的数据结构就是                        没用的。
学习重点:效率与资源消耗的度量衡-复杂度分析,10个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie树;10个算法:递归、排序、二份查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法。
学习技巧:
                1,边学边练,适度刷题。
                2,多问、多思考、多互动。
                3,打怪升级学习法,设立切实可行的目标。
                4,知识需要沉淀,不要试图一下子掌握所有

第3章 复杂度分析(上):如何分析、统计
复杂度分析的重要性:复杂度分析时整个算法学习的精髓,只要掌握了它,数据结构与算法的内容基本上就掌握了一半。
为何需要复杂度分析:
                                        1,测试结果非常依赖测试环境。
                                        2,测试结果受数据规模的影响很大。
大O表示法:T(n)=O(f(n))
                     T(n)表示代码执行时间,n表示数据规模的大小,f(n)代表每行代码执行的次数总和,O表示代码的执行时间T(n)与f(n)成正比。
整个意义:代表代码执行时间随数据规模增长的变化趋势。
复杂度分析法则:
                    1),单段代码看高频:比如循环
                    2),多段代码取最大:比如一段代码中有单循环与多重循环,那么取多重循环。
                    3),嵌套代码求乘积:比如递归、多重循环等。
                    4),多个规模求加法:比如方法有两个参数控制两个循环的次数,那么这时取二者复杂度相加。
常见的复杂度级别:
多项式阶:O(1)常数阶,O(logn)对数阶,O(n)线性阶,O(nlogn)线性对数阶,O(n^2)平方阶等
非多项式阶:随着数据规模的增长,算法的执行时间暴增,这类算法性能极差,包括指数阶,阶乘阶。

 

第5章 数组:为什么很多编程语言中数组都从0开始编号
    本章开始的时候抛出了一个问题:为什么很多编程语言中数组都是从0开始编号?这个问题很有意思,虽然我经常用到数组,但是还真的
没想到过为什么是从0开始的。
 
  如何实现随机访问?
  数组的定义:数组是一种线性表数据结构,它用一组连续的内存空间,来存储一组具有相同类型的数据。
  第一是线性表。线性表就是数据排成像一条线一样的结构。每个线性表上的数据最多只有前和后两个方向。除了数组,链表,队列,栈都是线性结构。
    第二是连续的空间和相同的类型的数据。计算内存地址:a[i]_address = base_address + i * data_type_size(数组和链表的区别?)
    数组删除(JVM标记清除垃圾回收算法)。插入删除的时间复杂度为O(n)
    很多时候,我们并不是要去死记硬背某个数据结构的或者算法,而是要去学习它背后的思想和处理技巧,这些东西才是最有价值的。 

    如果使用1作为数组的开始:    
    a[k]_address = base_address + (k-1)*type_size,要多做一次减法运算。

 

 

第6章 链表(上):如何实现LRU缓存淘汰算法?
链表结构:单链表、双向链表、循环链表。
单链表:头结点用来记录链表的基地址,尾节点指向空地址NULL 。
循环链表:尾节点指向头结点。著名的约瑟夫问题。
双向链表:具有后继指针next与前驱指针prev。linkhashpmap用到了双向列表。

对于执行较慢的程序,可以通过消耗更多的内存(空间换时间,比如缓存)来进行优化;
而消耗过多内存的程序,可以通过消耗更多的时间(时间换空间,比如手机单片机)来降低内存的消耗。

链表 & 数组
数组:插入删除O(n),随机访问O(1). 缺点:大小固定,需要连续的内容空间。如果太小,如要扩容,则输入需要迁移。如果太大,则容易导致内存不足,使用效率不高。
链表:插入删除O(1),随机访问O(n). 缺点:存储同样大小数据,需要更多空间,因为节点。链表的频繁插入删除容易导致内存碎片,造成GC .

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

ArrayList 详解

ArrayList是一个数组队列,相当于动态数组。ArrayList的操作不是线程安全的。

ArrayList包含了两个重要的对象:elementData 和 size。

(01) elementData 是"Object[]类型的数组",它保存了添加到ArrayList中的元素。实际上,elementData是个动态数组,我们能通过构造函数 ArrayList(int initialCapacity)来执行它的初始容量为initialCapacity;如果通过不含参数的构造函数ArrayList()来创建ArrayList,则elementData的容量默认是10。elementData数组的大小会根据ArrayList容量的增长而动态的增长,具体的增长方式,请参考源码分析中的ensureCapacity()函数。

(02) size 则是动态数组的实际大小。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

 


 

 


版权声明:本文为weixin_38650528原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。