表示终于有幸能一睹《算法导论》这本算法神作了。
虽然之前也或多或少接触过算法,比如研究HashMap等数据结构等。
看过我之前博客的小伙伴,应该可以看到我之前写过排序算法和查找算法等(C语言版本)。
不过我更希望是像大学学习《运筹学》那样,系统地整理算法体系。所以就有这次的博客,针对《算法导论》的学习笔记。我会将学习《算法导论》过程中遇到的一些重点,记录下来。
另外,每章的实现代码(Java版),我都会另开一篇博客,进行记录。
* 初始化:循环的第一次迭代之前,它为真
* 保持:如果循环的某次迭代之前它为真,那么下次迭代之前它仍为真
* 终止:在循环终止时,不变式为我们提供一个有用的性质,该性质有助于证明算法是正确的
PS:书中,这里就插入排序,分别阐述了该三条性质
PS:联系数学归纳法,初始化 针对是数学归纳法中的n = 1这样的初始条件证明,而 保持 针对的是数学归纳法中 n=m成立=》n=m+1成立 的递归条件证明。至于 终止 这是区分现实(程序)与理想(数学)的分界线,毕竟现实中程序的执行是存在终点的。
* 算数指令(加减乘除,取余,上下取整)
* 数据移动指令(装入,存储,复制)
* 控制指令(条件与无条件转移,子程序调用与返回)
PS:并且设置所有指令的耗时是常量的。实际情况并非如此,但是过于纠结细节,造成计算模型过于复杂,是不必要的。
* 一个算法的最坏情况运行时间给出了任何输入的运行时间的一个上界
* 对某些算法,最坏情况经常出现
* “平均情况”往往与最坏情况大致一样差
PS:上述第三条的理解:“平均情况”的增长量级(时间复杂度)往往和最坏情况一致
PS:特定情况下,我们会对一个算法的平均情况运行时间感兴趣,如概率分析
PS:递归令人想起数学归纳法中的 n=m成立=》n=m+1成立的过程
PS:程序中不断重复一个过程,那么实现无非就是循环与递归。如果重复次数直接,明确,可以采用循环。如果重复次数不明确,甚至需要过程不断重复后才知道,则采用递归。
* 分解:将问题分解为若干更小的子问题
* 解决:将对应子问题进行求解
* 合并:将这些子问题的解合并,并传给上一层递归
PS:如在归并排序中,我通过Integer.MAX_VALUE,来避免为空的判断。
练习部分,所以代码实现,都会放在对应的代码实现博客。剩下的练习,我只会挑选一些进行记录(有些东东,手写还好。用md写,太麻烦了)。当然,如果有小伙伴有疑惑,可以私信或@我
不可以。
虽然上层遍历元素的时间复杂度为n,而下层采用二分查找(时间复杂度为lgn),看起来整体时间复杂度为nlgn。但是下层并不仅仅是二分查找,查找只是定位到了目标位置,还需要进行当前元素的插入动作。而无论是在数组,还是别的顺序表中,插入的时间复杂度为n。所以就变成了n*(n+lgn),时间复杂度就变成了n^2。
综上,无法通过二分查找,将插入排序的时间复杂度从n^2优化到nlgn。
首先,通过归并排序,对数组进行排序(因为后面用到的二分查找需要顺序表。并且题目只要求确认是否存在,并没有要求返回对应index,所以在一开始进行排序操作)。
外层通过循环,依次获得对应元素,根据branchTarget = target - array[i]这样的操作获得两个元素中的另一个元素。再通过二分查找,判断集合中是否存在对应的另一个元素。
归并排序的时间复杂度为nlgn,循环遍历的时间复杂度为n,二分查找的时间复杂度为lgn,所以最终的耗时为nlgn+n*lgn,故最终时间复杂度为nlgn。
具体实现,请看本章代码实现博客。
由于该章的思考题,都需要进行公式计算与展示,而我并不会用MarkDown表示,所以就不写出来了。如果感兴趣,给各位一个资料(Algorithems Solutions)。
其实这个章节还是比较简单的。算法上面举了一些有关排序的例子,以及一个折半查找的demo。数学方面,一方面需要还记得高中的数学归纳法,另一方面需要一定的排列组合的认识(进行复杂度的计算表)。
原文:https://www.cnblogs.com/Tiancheng-Duan/p/12343865.html