这是排序部分的第一讲,我们会先介绍一下排序问题及其应用,然后介绍插入排序和归并排序两种算法,并对比。最后用Python实现这两种算法。
清爽版入口
Input:
array A[1…n] of numbers.
Output:
permutation B[1…n] of A such that B[1] ≤ B[2] ≤ … ≤ B[n] .
e.g.
A = [7, 2, 5, 5, 9.6] → B = [2, 5, 5, 7, 9.6]
排序问题给定一个输入数组A,算法处理后输入一个有序数组B。
排序算法在很多方面都有应用,比如:
可以说排序算法是计算机科学一个很重要的基础,所以掌握好排序算法是很重要也很必要的。
插入排序可以说是最简单的一种排序算法,只需要四五行的Python代码就能实现。当然简单在很多时候意味着低效。
伪代码:
1. Insertion-Sort(A[],n)
2. for j <-- 2 to n
3. 将A[j]插入到已经排好序的子数组A[1..j-1]中
4. 通过调换位置讲A[j]放在正确的位置
上述伪代码中第3行,将A[j]插入到已经排好序的子数组,这个过程中有一定的优化空间。因为子数组已经是排好序的,所以我们可以选择二分法插入。这样时间复杂度就变成了 Θ ( n ) \Theta(n) Θ(n)变为 Θ ( l o g 2 n ) \Theta(log_2n) Θ(log2n)。
即便是用二分查找法找到合适的位置,但是第4行伪代码中将A[j]插入到合适的位置依然要花费 Θ ( n ) \Theta(n) Θ(n)。因为插入的过程是通过交换位置实现的。
所以不难看出插入排序要遍历2…n-1元素,将每个元素插入到合适位置的时间复杂度是 Θ ( n ) \Theta(n) Θ(n)。所以插入排序的整体时间复杂度是 Θ ( n 2 ) \Theta(n^2) Θ(n2)。
归并排序算法是分治策略一种典型的应用。《算法导论》中给出了分治策略的三个步骤:
对应到归并排序算法
归并排序算法伪代码如下:
Merge-Sort A[1..n]
if n = 1
done
else
Merge-Sort A[1..n/2]
Merge-Sort A[n/2 + 1 .. n]
Merge the two sorted sub-arrays
分析:
可以列出归并排序的递归式:
T ( n ) = { Θ ( 1 ) 若 n ≤ c 2 T ( n 2 ) + Θ ( n ) T(n) = \begin{cases} \Theta(1)\ \ 若\ n \le c\\ 2T(\frac{n}{2}) + \Theta(n) \end{cases} T(n)={Θ(1) 若 n≤c2T(2n)+Θ(n)
利用master theory容易得出归并排序算法的时间复杂度是 Θ ( n l o g 2 n ) \Theta(nlog_2n) Θ(nlog2n)
python本身已经内置了排序算法,而且时间复杂度不错。我们自己实现一遍主要是为了理解算法。
def InsertionSort(nums):
for i in range(1,len(nums)):
j = i - 1
tmp = nums[i]
while j >= 0:
if nums[j] > tmp:
nums[j+1] = nums[j]
j -= 1
else:
break
nums[j + 1] = tmp
def Merge(nums,s,m,e):
left = nums[s:m+1]
right = nums[m+1:e+1]
i = j = 0
k = s
while i < len(left) and j < len(right):
if left[i] < right[j]:
nums[k] = left [i]
i += 1
else:
nums[k] = right[j]
j += 1
k += 1
while i < len(left):
nums[k] = left[i]
k += 1
i += 1
while j < len(right):
nums[k] = right[j]
k += 1
j += 1
def MergeSort2(nums,s,e):
if s != e:
m = (s + e) // 2
MergeSort2(nums,s,m)
MergeSort2(nums,m+1,e)
Merge(nums,s,m,e)
def MergeSort(nums):
MergeSort2(nums,0,len(nums)-1)