递归
递归代码都可以改为这种迭代循环的非递归写法。例如
1 | f(x) =f(x-1)+1 |
因为递归本身就是借助栈来实现的,只不过我们使用的栈是系统或者虚拟机本身提供的,我们没有感知罢了。如果我们自己在内存堆上实现栈,手动模拟入栈、出栈过程,这样任何递归代码都可以改写成看上去不是递归代码的样子。但是这种思路实际上是将递归改为了“手动”递归,本质并没有变
递归代码虽然简洁高效,但是,递归代码也有很多弊端。比如,堆栈溢出、重复计算、函数调用耗时多、空间复杂度高等,所以,在编写递归代码的时候,一定要控制好这些副作用。
排序
排序算法 | 时间复杂度 | 是否基于比较 |
---|---|---|
冒泡、插入、选择 | O(n2) | - [x] |
快排、归并 | O(nlogn) | 是 |
桶、计数、基数 | O(n) | 否 |
时间复杂度 | 是否稳定排序 | 是否原地排序 | |
---|---|---|---|
冒泡 | O(n2) | 是 | 是 |
插入 | O(n2) | 是 | 是 |
选择 | O(n2) | 否 | 是 |
快排 | O(nlogn) | 否 | 是 |
归并 | O(nlogn) | 是 | 否 |
计数 | O(n+k) k是数据范围 | 是 | 否 |
桶 | O(n) | 是 | 否 |
基数 | O(dn) d是维度 | 是 | 否 |
分析“排序算法”
1排序算法的执行效率
a.最好情况、最坏情况、平均情况时间复杂度
b.时间复杂度的系数、常数 、低阶
c. 比较次数和交换(或移动)次数
2.排序算法的内存消耗
3.排序算法的稳定性
冒泡、插入、选择
冒泡法:
1 | 354126 当某次冒泡操作已经没有数据交换时,说明已经达到完全有序,不用再继续执行后续的冒泡操作 |
插入排序:
通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描;
在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间
1 | 354126 |
选择排序:
选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。
1 | <?php |
插入排序比冒泡排序更受欢迎的原因:
冒泡排序需要 3 个赋值操作,而插入排序只需要 1 个
以java测试:
1 | 随机生成 10000 个数组,每个数组中包含 200 个数据,然后在机器上分别用冒泡和插入排序算法来排序,冒泡排序算法大约 700ms 才能执行完成,而插入排序只需要 100ms 左右就能搞定! |
冒泡排序和插入排序在时间复杂度上是一样的,都是 O(n2),但是如果我们希望把性能优化做到极致,那肯定首选插入排序
排序名 | 是否原地排序 | 是否稳定 | 最好 | 最坏 | 平均 |
---|---|---|---|---|---|
冒泡 | 是 | 是 | O(n) | O(n2) | O(n2) |
插入 | 是 | 是 | O(n) | O(n2) | O(n2) |
选择 | 是 | 否 | O(n2) | O(n2) | O(n2) |
冒泡、插入、选择适合小规模数据排序。
以上学于极客时间,做的笔记