admin管理员组

文章数量:1625101

 写在开头

这是一篇读书笔记式的文章,力求简要地概括《算法图解》 中陌生和重要的内容,所以有的具体内容仍需要参考原书。

值得记录的代码附在文末,使用python编写。

持续更新。

这本书已经读完,这篇笔记也更新至此。不得不说,《算法图解》是一本对新手非常友好的书,内容详细而不啰嗦,十分有条理,只要沿着顺序读下去,基本能够很快理解消化。我很庆幸我是在这本书中第一次接触到诸如动态规划之类的知识点,否则很有可能又被劝退了。当然,由于其篇幅限制,很多常用内容没有介绍到,还需要继续补充,这些内容记录在我的另一篇笔记里。


第1章 算法简介

二分查找

大O表示法:O(n),n指操作数

  • 该表示法中的log默认以2为底
  • 指出了算法运行时间的增速
  • 画16个格子的例子:一个一个画,O(n);四次对折,O(logn)
  • 指出的是最糟情况下的操作数

常见的大O运行时间

  • O(log n)
  • O(n)
  • O(n*log n)
  • O(n^2)
  • O(n!),如旅行商问题

随着输入的增加,上述五种算法的操作数的增加由慢到快


第2章 选择排序

数组与链表的区别:“挨着坐”和“分开坐”

数组在内存中是连续存放的;链表中的元素可以存储在内存的任何地方,链表的每个元素都存储了下一个元素的地址。

数组vs链表

  • 当数组增加元素而相邻内存单元已被占用,就需要移动整个数组,链表不存在这个问题
  • 需要读取链表最后一个元素时,需要从第一个开始依次读取。当需要同时读取所有元素时,链表效率高,需要跳跃时效率低;数组相反
  • 数组支持随机访问,链表只能顺序访问

一个疑惑和其解答

Q:在向链表的中间插入元素时,不需要先从第一个元素逐个获取索引,直到要插入的位置?

A:获取索引即读的过程,不应算在操作数中;只考虑插入这个操作。

数组与列表可以组合使用

facebook存储用户信息的例子

TODO:选择排序的例子,为什么是使用平均每次检查的操作数的平均值来计算O,而不是直观理解的阶乘?


第3章 递归

性能

递归并不会比循环提高性能,但可能更容易理解

组成

  • 基线条件:控制何时停止调用自己,避免无限循环
  • 递归条件:循环调用自己

只对最上层元素执行两种操作:插入、删除并读取(弹出)

调用栈

例子的文字总结:一个函数A中调用了另一个函数B,当A开始执行,内存分配给其一部分,存储其涉及到的所有变量;当执行到B,内存分配给B一部分,并在栈中压在A的部分之上;执行B,其被从栈上弹出,此时A位于最上方,故继续执行A。这个用于储存多个函数的变量的栈,被称为调用栈。

调用另一个函数时,当前函数暂停并处于未完成状态。

递归调用栈:以阶乘为例

要注意,一个变量在每次调用中的值可能不同,在一个调用中不能访问另一个调用中的变量(还是比较符合常识的)

栈的弊端

每次函数调用会占用内存,栈过高占用的内存也过多

解决办法:改用循环;使用尾递归(本书不涉及)


第4章 快速排序

分而治之,Divide and Conquer,D&C

  • 找出尽可能简单的基线条件
  • 不断将问题分解直到满足基线条件

Tip:涉及到数组的递归函数常见的基线条件是数组为空或只含一个元素。

快速排序思路

  • 最简单的排序:不需要排序,即数组中只有0或1个元素
  • 两个元素的数组,比较二者的值
  • 多于两个元素的数组,分而治之
    • 选取一个元素作为基准值(暂取第一个)
    • 分区:遍历,找出比基准值大的和小的元素,分别构成两个数组。目前有:比基准值小的子数组、基准值、比基准值大的子数组
    • 对子数组递归,直到剩下的数组长度小于等于二
    • 子数组排序后,合并

        使用python实现的快排,见代码1

再谈大O表示法 - 比较合并排序和快速排序 - 大O表示法中的常量

例子,逐个打印数组元素的函数,一个没有sleep(1)(记一次sleep的时间为c),另一个有,则其运行时间分别为c*n和n。但是在大O表示法中,固定时间量,也即常数c,是忽略不计的,因为一般来说对时间影响更大的是n和logn的区别。

对于运行时间都为nlogn的快速查找和合并查找,常量的影响就可能很大;快速查找更快

本文标签: 算法笔记