Stack
Monotonous stack
Sorting
Any algorithm that sorts a list of n entries by use of key comparisons must, in its
worst case, perform at least ceil(log2(n!)) comparisons of keys, and, in the average case, it must perform at least log2(n!) comparisons of keys.
approximation log2(n!) = n*log2(n)+O(n)
| best case | average case | worst case | additional memory | stability | |
|---|---|---|---|---|---|
| Bubble sort | O(N) | O(0.25N^2) | O(0.5N^2) | 1 | stable |
| Insertion Sort | O(N) | O(0.25N^2) | O(0.5N^2) | 1 | stable |
| Selection Sort | O(0.5N^2) | O(0.5N^2) | O(0.5N^2) | 1 | unstable |
| Shell Sort | O(N^2) | O(N^2) | O(N^2) | 1 | unstable |
| Mergesort | stable | ||||
| Quick Sort | unstable | ||||
| Heap Sort | unstable |
Bubble sort
Insertion Sort
Selection Sort
举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。
Shell sort
Mergesort
- Counting Comparisons: The number of comparisons of keys done by mergesort on a list of
nentries, therefore, is no more thanceil(n*log2(n)). - The total number of key comparisons done by mergesort is less than
n*log2(n)-n+1.
Quick Sort
- Count of Comparisons and Swaps:
C(n)=n-1+C(r)+C(n-r-1) - Comparisons for worst case:
C(n)=n-1+C(n-1)=0.5n^2-0.5n - Counting Swaps for Average-Case Analysis of Quicksort:
S(n,p)=(p+1)+S(p-1)+S(n-p)wherepdenotes that partition isp +1, consisting of one swap in the loop for each of the p − 1 keys less than p and two swaps outside the loop. Approximately,
S(n)=0.69(n*log2(n))+O(n)
- Counting Comparisons for Average-Case Analysis of Quicksort:
C(n)=n-1+C(p-1)+C(n-p). Approximately,
C(n)=2n*ln(n)+O(n)=1.39n*log2(n)+O(n)
快速排序为什么快?
快速排序为什么快? | Why is quicksort better than mergesort? | Why Quick Sort preferred for Arrays and Merge Sort for Linked Lists?
在畅销的ACM信息竞赛教材《算法艺术与信息学竞赛》上,作者也提出了关于快排的效率与优化问题——
快速排序有O(n^2)最坏情况运行时和O(nlogn)平均运行时。但是,在许多情况下,它比归并排序要好,因为有很多因素会影响算法的运行时间,而且,当把这些因素综合在一起时,快速排序胜出。 特别是,排序算法经常被引用的运行时指的是对数据进行排序所需执行的比较次数或交换次数。这确实是一种很好的性能度量方法,特别是因为它独立于底层硬件设计。然而,其他一些东西——比如引用的位置(比如,我们是否读取了很多可能在缓存中的元素?)——在当前的硬件上也扮演着重要的角色。特别是,快速排序只需要很少的额外空间,并且具有良好的缓存局部性,这使得它在很多情况下比归并排序更快。
- 快速排序的一般形式是原地排序(即,它不需要任何额外的存储空间),而归并排序需要O(N)额外的存储空间,N表示数组的大小,这可能是相当昂贵的。分配和释放用于归并排序的额外空间会增加算法的运行时间。
- 比较平均复杂度,我们发现两种类型的平均复杂度都是O(NlogN),但常数不同。对于数组,由于使用了额外的O(N)存储空间,合并排序会丢失。
- 大多数快速排序的实际实现使用随机版本。随机化版本的期望时间复杂度为O(nLogn)。最坏的情况也可能出现- 在随机版本中,但最坏的情况不会出现在特定的模式中(如排序数组),而随机快速排序在实践中效果很好。
- Quick Sort is also a cache friendly sorting algorithm as it has good locality of reference ([计] 访问局部性) when used for arrays. 快速排序也是一种缓存友好的排序算法,因为它在用于数组时具有良好的引用局部性。
- Quick Sort is also tail recursive, therefore tail call optimizations is done. 快速排序也是尾部递归,因此尾部调用优化已经完成。
locality of reference
- 局部性原理是指计算机在执行某个程序时,倾向于使用最近使用的数据。局部性原理有两种表现形式:时间局部性和空间局部性。
- 时间局部性是指被引用过的存储器位置很可能会被再次引用,例如:重复的引用一个变量时则表现出较好的时间局部性
- 空间局部性是指被引用过的存储器位置附近的数据很可能将被引用;例如:遍历二维数组时按行序访问数据元素具有较好的空间局部性
- 一个优秀的程序通常具有良好的局部性,它们通常会重复使用已用过的数据,或者使用已用过数据的邻近数据,也就是说,程序常常会使用集中在一起的局部数据。
- 局部性分为:时间局部性和空间局部性。如果一个内存位置被重复的引用,那就是有了时间局部性,如果一个内存位置被引用了,很快这个位置的附近位置也被引用了,这就有了空间局部性。
- 缓存算法:FIFO、LRU、LFU, Refet to 缓存算法LRU和LFU的C++实现
Radix Sort
top K问题
Recursion
Tail Recursion
Why do we care? The tail recursive functions considered better than non tail recursive functions as tail-recursion can be optimized by compiler. The idea used by compilers to optimize tail-recursive functions is simple, since the recursive call is the last statement, there is nothing left to do in the current function, so saving the current function’s stack frame is of no use. 尾部递归函数被认为比非尾部递归函数更好,因为尾部递归可以由编译器进行优化。编译器用来优化尾部递归函数的想法很简单,因为递归调用是最后一条语句,所以在当前函数中不需要做任何事情,所以保存当前函数的堆栈框架是没有用的。
Tree
Huffman Coding
Lexicographic Search Trees: TRIES
Segment Tree
Fenwick tree (binary indexed tree)
树状数组(Binary Indexed Tree, Fenwick Tree)的C++实现