Skip to content

Commit

Permalink
fix(basic/tim-sort):添加时间复杂度证明 OI-wiki#5536
Browse files Browse the repository at this point in the history
  • Loading branch information
xk2013 committed Apr 20, 2024
1 parent 84a95ac commit d0d7f46
Showing 1 changed file with 73 additions and 1 deletion.
74 changes: 73 additions & 1 deletion docs/basic/tim-sort.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)$ 的性能表现。

## 写在后面

Expand Down

0 comments on commit d0d7f46

Please sign in to comment.