Programming
相关知识
- Concurrency/Parallel 并发/并行
- Synchronous/Asynchronous 同步/异步 & 阻塞/非阻塞
- John Von Neumann Architecture 冯 - 诺以曼体系
- Memory and Register 内存与寄存器
- Database 数据库
- Regexp 正则表达式
- JSON JS 对象符号
- Distributed System 分布式系统
ASCII
0 NUT 48 10 65 A 97 a
算法与数据结构
倍率定理
如果 \[T(N)\sim aN^b\lg N\],那么 \[\frac{T(2N)}{N} \sim 2^b\] ,进而可以推出常见的增长数量级函数均会出现这种情况,指数级别除外.
证明: \[\frac{T(2N)}{N}=\frac{a(2N^b)\lg(2N)}{aN^b\lg N}\] \[=a^b(1+\frac{\lg 2}{\lg N})\sim 2^b\]
常见近似函数
描述 | 符号 |
---|---|
调和级数求和 | \[H_N=\frac{1}{1}+\frac{1}{2}+\frac{1}{3}+\cdots +\frac{1}{N}=\ln(N+1)+\gamma \sim \ln N\quad \gamma\approx0.5772156...\] |
等差数列求和 | \(1+2+3+\cdots+N=\frac{N(N+1)}{2}\sim N^2\) |
等比数列求和 | \(T_a(n)=a^0+a^1+a^2+\cdots +a^n=\frac{(a^{n+1}-1)}{a-1}\sim a^N\) |
斯特灵公式 | \(\lg N!=\lg 1+\lg 2+\lg 3+\cdots+\lg N\sim N\lg N\) |
几何分布 | \((1-\lambda)\cdot[1+2\lambda+3\lambda^2+4\lambda^3+\cdots]=\frac{1}{1-\lambda}\sim O(1)\quad0<\lambda<1\) |
收敛级数 | \(1+\frac{1}{2^2}+\frac{1}{3^2}+\cdots \sim 1\) |
幂方级数 | \(\sum_{k=0}^n k^d \approx \int_0^n x^{d+1}dx = \frac{1}{d+1}x^{d+1}\lvert_0^n = \frac{1}{d+1}n^{d+1} \sim n^{d+1}\) |
指数函数 | \((1-\frac{1}{x})^x\sim\frac{1}{e}\) |
二项式系数 | \(\tbinom{N}{k}\sim\frac{N^k}{k!}\) |
Algorithm - AQ
18问
- 请简单解释算法是什么? 算法是一个定义良好的计算过程,它将一些值作为输入并产生相应的输出值。简单来说,它是将输入转换为输出的一系列计算步骤。
- 解释什么是快速排序算法? 快速排序算法能够快速排序列表或查询。它基于分割交换排序的原则,这种类型的算法占用空间较小,它将待排序列表分为三个主要部分: 小于 Pivot 的元素 枢轴元素 Pivot(选定的比较值) 大于 Pivot 的元素
- 解释算法的时间复杂度? 算法的时间复杂度表示程序运行完成所需的总时间,它通常用大 O 表示法来表示。
- 请问用于时间复杂度的符号类型是什么? 用于时间复杂度的符号类型包括: Big Oh:它表示小于或等于目标多项式 Big Omega:它表示大于或等于目标多项式 Big Theta:它表示与目标多项式相等 Little Oh:它表示小于目标多项式 Little Omega:它表示大于目标多项
- 解释二分法检索如何工作 在二分法检索中,我们先确定数组的中间位置,然后将要查找的值与数组中间位置的值进行比较,若小于数组中间值,则要查找的值应位于该中间值之前,依此类推,不断缩小查找范围,直至得到终结果
- 解释是否可以使用二分法检索链表? 由于随机访问在链表中是不可接受的,所以不可能到达 O(1) 时间。因此,对于链表来说,二分法检索是不可以的(对顺序链表或排序后的链表是可以的)
- 解释什么是堆排序? 堆排序可以看成是选择排序的改进,它可以定义为基于比较的排序算法。它将其输入划分为未排序和排序的区域,通过不断消除最小元素并将其移动到排序区域来收缩未排序区域。
- 说明什么是 Skip List ? Skip list 数据结构化的方法,它允许算法在符号表或字典中搜索、删除和插入元素。在 Skip list 中,每个元素由一个节点表示。搜索函数返回与key相关的值的内容。插入操作将指定的键与新值相关联,删除操作可删除指定的键。
- 解释插入排序算法的空间复杂度是多少? 插入排序是一种就地排序算法,这意味着它不需要额外的或仅需要少量的存储空间。对于插入排序,它只需要将单个列表元素存储在初始数据的外侧,从而使空间复杂度为 O(1) 。
- 解释什么是“哈希算法”,它们用于什么? “哈希算法”是一个哈希函数,它使用任意长度的字符串,并将其减少为唯一的固定长度字符串。它用于密码有效性、消息和数据完整性以及许多其他加密系统。
- 解释如何查找链表是否有循环? 要知道链表是否有循环,我们将采用两个指针的方法。如果保留两个指针,并且在处理两个节点之后增加一个指针,并且在处理每个节点之后,遇到指针指向同一个节点的情况,这只有在链表有循环时才会发生。
- 解释加密算法的工作原理? 加密是将明文转换为称为“密文”的密码格式的过程。要转换文本,算法使用一系列被称为“键”的位来进行计算。密钥越大,创建密文的潜在模式数越多。大多数加密算法使用长度约为 64 到 128 位的固定输入块,而有些则使用流方法。
- 列出一些常用的加密算法? 一些常用的加密算法是: 3-way Blowfish CAST CMEA GOST DES(Triple DES) IDEA LOKI
- 解释一个算法的最佳情况和最坏情况之间有什么区别? 最佳情况:算法的最佳情况解释为算法执行最佳的数据排列。例如,我们进行二分法检索,如果目标值位于正在搜索的数据中心,则这就是最佳情况,最佳情况时间复杂度为 0 。 最差情况:给定算法的最差输入参考。例如快速排序,如果选择关键值的子列表的最大或最小元素,则会导致最差情况出现,这将导致时间复杂度快速退化到 O(n2) 。
- 解释什么是基数排序算法? 基数排序又称“桶子法”,是通过比较数字将其分配到不同的“桶里”来排序元素的。它是线性排序算法之一。
- 解释什么是递归算法? 递归算法是一个解决复杂问题的方法,将问题分解成较小的子问题,直到分解的足够小,可以轻松解决问题为止。通常,它涉及一个调用自身的函数。
- 提到递归算法的三个定律是什么?
所有递归算法必须遵循三个规律
- 递归算法必须有一个基点
- 递归算法必须有一个趋向基点的状态变化过程
- 递归算法必须自我调用
- 解释什么是冒泡排序算法? 冒泡排序算法也称为下沉排序。在这种类型的排序中,要排序的列表的相邻元素之间互相比较。如果它们按顺序排列错误,将交换值并以正确的顺序排列,直到最终结果“浮”出水面。
堆和栈
- 程序内存布局场景下,堆与栈表示两种内存管理方式
- 数据结构场景下,堆与栈表示两种常用的数据结构
内存管理
栈由操作系统自动分配释放 ,用于存放函数的参数值、局部变量等,其操作方式类似于数据结构中的栈。 其中函数中定义的局部变量按照先后定义的顺序依次压入栈中,也就是说相邻变量的地址之间不会存在其它变量。栈的内存地址生长方向与堆相反,由高到底,所以后定义的变量地址低于先定义的变量,栈中存储的数据的生命周期随着函数的执行完成而结束。
堆由开发人员分配和释放, 若开发人员不释放,程序结束时由 OS 回收,分配方式类似于链表。 堆的内存地址生长方向与栈相反,由低到高,但需要注意的是,后申请的内存空间并不一定在先申请的内存空间的后面,即 p2 指向的地址并不一定大于 p1 所指向的内存地址,原因是先申请的内存空间一旦被释放,后申请的内存空间则会利用先前被释放的内存,从而导致先后分配的内存空间在地址上不存在先后关系。堆中存储的数据若未释放,则其生命周期等同于程序的生命周期。 关于堆上内存空间的分配过程,首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆节点,然后将该节点从空闲节点链表中删除,并将该节点的空间分配给程序。另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的 delete/free 语句才能正确地释放本内存空间。由于找到的堆节点的大小不一定正好等于申请的大小,系统会自动地将多余的那部分重新放入空闲链表。
区别:
- 管理方式不同。栈由操作系统自动分配释放,无需我们手动控制;堆的申请和释放工作由程序员控制,容易产生内存泄漏
- 空间大小不同。每个进程拥有的栈的大小要远远小于堆的大小。理论上,程序员可申请的堆大小为虚拟内存的大小,进程栈的大小 64bits 的 Windows 默认 1MB,64bits 的 Linux 默认 10MB
- 生长方向不同。堆的生长方向向上,内存地址由低到高;栈的生长方向向下,内存地址由高到低
- 分配方式不同。堆都是动态分配的,没有静态分配的堆。栈有 2 种分配方式:静态分配和动态分配。静态分配是由操作系统完成的,比如局部变量的分配。动态分配由 malloc 函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由操作系统进行释放,无需我们手工实现
- 分配效率不同。栈由操作系统自动分配,会在硬件层级对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是由 C/C++ 提供的库函数或运算符来完成申请与管理,实现机制较为复杂,频繁的内存申请容易产生内存碎片。显然,堆的效率比栈要低得多
- 存放内容不同。栈存放的内容,函数返回地址、相关参数、局部变量和寄存器内容等。当主函数调用另外一个函数的时候,要对当前函数执行断点进行保存,需要使用栈来实现,首先入栈的是主函数下一条语句的地址,即扩展指针寄存器的内容(EIP),然后是当前栈帧的底部地址,即扩展基址指针寄存器内容(EBP),再然后是被调函数的实参等,一般情况下是按照从右向左的顺序入栈,之后是被调函数的局部变量,注意静态变量是存放在数据段或者 BSS 段,是不入栈的。出栈的顺序正好相反,最终栈顶指向主函数下一条语句的地址,主程序又从该地址开始执行。堆,一般情况堆顶使用一个字节的空间来存放堆的大小,而堆中具体存放内容是由程序员来填充的。
无论是堆还是栈,在内存使用时都要防止非法越界,越界导致的非法内存访问可能会摧毁程序的堆、栈数据,轻则导致程序运行处于不确定状态,获取不到预期结果,重则导致程序异常崩溃。
数据结构
栈是一种运算受限的线性表,其限制是指只仅允许在表的一端进行插入和删除操作,这一端被称为栈顶(Top),相对地,把另一端称为栈底(Bottom)。把新元素放到栈顶元素的上面,使之成为新的栈顶元素称作进栈、入栈或压栈(Push);把栈顶元素删除,使其相邻的元素成为新的栈顶元素称作出栈或退栈(Pop)。这种受限的运算使栈拥有“先进后出”的特性(First In Last Out),简称 FILO。 栈分顺序栈和链式栈两种。栈是一种线性结构,所以可以使用数组或链表(单向链表、双向链表或循环链表)作为底层数据结构。使用数组实现的栈叫做顺序栈,使用链表实现的栈叫做链式栈,二者的区别是顺序栈中的元素地址连续,链式栈中的元素地址不连续。 栈的基本操作包括初始化、判断栈是否为空、入栈、出栈以及获取栈顶元素等。
堆是一种常用的树形结构,是一种特殊的完全二叉树,当且仅当满足所有节点的值总是不大于或不小于其父节点的值的完全二叉树被称之为堆。堆的这一特性称之为堆序性。因此,在一个堆中,根节点是最大(或最小)节点。如果根节点最小,称之为小顶堆(或小根堆),如果根节点最大,称之为大顶堆(或大根堆)。堆的左右孩子没有大小的顺序。 堆的存储一般都用数组来存储堆,i 节点的父节点下标就为 ( i – 1 ) / 2 (i – 1) / 2(i–1)/2 。它的左右子节点下标分别为 2 ∗ i + 1 2 * i + 12∗i+1 和 2 ∗ i + 2 2 * i + 22∗i+2 。如第 0 个节点左右子节点下标分别为 1 和 2 。
Open Source License
常见开源许可证及其差异1:
- BSD 许可证 —— 特点是可以自由使用、修改、再发布。但是在商用或者个人分发过程中必须带有原来代码的许可证,且不能用原作者相关信息去做宣传。
- MIT 许可证 —— 源自麻省理工学院(Massachusetts Institute of Technology, MIT),是使用最广泛的一种开源许可证。其特点和 BSD 许可证类似,只要在项目的所有副本中包含版权声明和许可声明,就无需承担任何责任。
Apache 许可证 —— 作为 permissive license 中的一员,Apache 多了几个限制条件,禁止使用其商标与作者的相关信息进行商业行为,必须明确指出所有修改过的文件。
把用到的或重写的 Apache 2.0 开源项目这些代码文件的头部放上原项目的许可证和著作权声明,在项目的根目录的 notices 文件中说明参考的具体项目以及哪些用到或重写的文件并附上 Apache 2.0 许可证全文。
GPL 许可证 —— GPL 和 BSD 区别还是很大的,GPL 主张代码及衍生代码的开源,不允许修改后和衍生的代码做为闭源的商业软件进行发布和出售。如果已发布商业软件源码里含有 GPL 开源软件源码,则必须对该商业软件进行开源或者下架处理。
GPL 如果是动态或静态链接,也许要开源。但 LGPL 在动态链接的情况下,可以不开源专有代码2。
- AGPL 许可证 —— AGPL 是 GPL 的一个补充,在 GPL 的基础上加了一些限制。GPL 的约束生效前提是该软件 “发布”,有的公司就使用 GPL 组件编写 web 系统,但是不发布系统,只用这个系统在线提供服务,这样就避免了开源系统代码。而 AGPL 要求如果云服务 (即 saas) 用到的代码是该许可证,那云服务的代码也必须开源。
- LGPL 许可证 —— LGPL 允许商业软件通过类库引用的方式使用 LGPL 类库,而不需要开源商业软件源码。
- MPL 许可证 —— 在商业软件中,如果含有 MPL 许可证的代码在单独的文件内,其他新增的文件就可以避免开源。
Editor
EMACS
VIM
Thanks the author of this nice picture.
