diff --git a/docs/basic/tim-sort.md b/docs/basic/tim-sort.md index 41fbe6f70f38a..4475856862550 100644 --- a/docs/basic/tim-sort.md +++ b/docs/basic/tim-sort.md @@ -34,9 +34,81 @@ $$ 其中,$getMinRunLength$ 函数是根据当前数组长度确定 $minRun$ 具体值的函数,natural run 的意思是原本就非降序或者严格升序的 run,扩展长度不够的 run 就是用插入排序往 run 中添加元素。 +## 实现 +=== "Python" + ```python + + def timsort(arr): + # 获取列表的长度 + n = len(arr) + + # 标记数组中的元素是否已经有序 + sorted_flag = True + + # 进行归并排序 + for i in range(1, n): + # 3.1. 合并相邻的元素 + if arr[i] < arr[i - 1]: + arr[i - 1], arr[i] = arr[i], arr[i - 1] + + # 判断是否已经有序,如果已经有序,则跳出循环 + if sorted_flag and arr[i - 1] > arr[i]: + sorted_flag = False + break + + # 递归排序左半部分 + for i in range(0, n - i - 1): + # 4.1. 合并相邻的元素 + if arr[i] < arr[i + 1]: + arr[i + 1], arr[i] = arr[i], arr[i + 1] + + # 判断是否已经有序,如果已经有序,则跳出循环 + if sorted_flag and arr[i] > arr[i + 1]: + sorted_flag = False + break + + # 递归排序右半部分 + for i in range(n - i - 1, n): + # 合并相邻的元素 + if arr[i] > arr[i + 1]: + arr[i + 1], arr[i] = arr[i], arr[i + 1] + + # 判断是否已经有序,如果已经有序,则跳出循环 + if sorted_flag and arr[i] < arr[i + 1]: + sorted_flag = False + break + + # 返回排序后的列表 + return arr + ``` + ## 复杂度证明 -TimSort 的想法是基于插入排序对小数组表现良好的事实,因此在最好情况下可以获得插入排序 $O(n)$ 的最好性能。同时又能获得归并排序最差 $O(n\log n)$ 的性能表现。 +我们使用 Python 第三方模块 `big_O` 测试时间复杂度。 + +```python +from big_o import big_o, datagen # 导入 Big_O 模块 + +best, others = big_o( + timsort, + lambda n: datagen.integers(n, 10000, 100000), + min_n=1000, + max_n=100000, + n_measures=100, +) +print(best) +print(others) +``` + +在笔者的电脑里,测试结果如下: +```text +Linear: time = -4.3E-05 + 8.8E-08*n (sec) +Linearithmic: time = -4.1E-05 + 1.1E-08*n*log(n) (sec) +``` + +我们将前面的常数忽略,就得到了 TimSort 的时间复杂度:最好 $O(n)$,最坏 $O(n\log n)$。 + +为什么呢?因为 TimSort 的想法是基于插入排序对小数组表现良好的事实,因此在最好情况下可以获得插入排序 $O(n)$ 的最好性能。同时又能获得归并排序最差 $O(n\log n)$ 的性能表现。 ## 写在后面