Saturday, February 27, 2010

排序:插入排序和归并排序比较

排序算法的可以用两种不同的标准分类:
1. 存储空间
2. 稳定(stable)

所谓存储空间指的是算法需要的外部空间是不是一个常数。当需要的外部存储空间是常数时,这个排序就是原地排序(in place sort)。反之就不是。计算机科学就是一个时间和空间的妥协,可以用空间换时间,也可以用时间换空间。 所谓稳定,就是相等元素的相对位置不变。例子: 1,3,1,6中两个1的相对位置必须保持一致。

插入排序(Insertion Sort)是算法导论(Introduction to Algorithm)介绍的第一个排序算法,它的优点是算法简洁,实现(implementation)方便,稳定(相同元素的相对位置保持不变),而且是原地排序。缺点是时间复杂度为n2,也就是慢。
归并排序(Merge Sort)则是介绍的第二个算法,优点是快,时间复杂度是nlg(n),并且稳定。缺点是递归,比插入排序复杂,不是原地排序。

插入排序的算法表述:
把n维数组分成前后两个部分,视前半部分为已排序的(sorted),后半部分为未排序的(unsorted)。
每次循环取出未排序部分(unsorted)的第一个元素,逐个比较它和已排序部分(sorted)的大小,并且插入到已排序部分(sorted)。

归并排序是一种“分治”(Divide and conquer)算法:把问题(problem)分成若干的小的问题逐个解决。
“分治”算法的三个步骤:
1. 分解(devide): 将原问题分解为一系列子问题。
2. 解决(conquer): 递归地解答各个子问题,若子问题够小,可直接求解。
3. 合并(combine): 将子问题的结果合并成原问题的解。
对于归并排序来说就是:
1. 将n个元素分解为n/2个子序列。
2. 再用归并算法递归解决这两个字序列的问题。
3. 合并两个已排完序的序列。
抽象的数学证明很难给人一个具象的感受,所以需要实验来感受一下。
实验环境:
CPU: Intel Core2 Duo T6400 @ 2.0GHZ
语言: C#(.NET 3.5 SP1) / C++(gcc 3.4.5)

实验数据:
0至100000之间的90000个随机数。

MergeSort(C#):

Insertion Sort(C#):

C#结果:
归并排序(merge sort): 00:00:00.0960054
插入排序(insertion sort): 00:00:49.5428337

Merge Sort(C++):

Insertion Sort(C++):

C++结果(毫秒):
归并排序(merge sort): 78
插入排序(insertion sort): 5273

从实验结果来说,当数据量很大的时候,归并排序的性能(performance)远好于插入排序。
实验同时也表明对于一个好的算法其性能表现比较一致,C#和C++在归并排序的测评上表现相近。但是对一个相对较差的算法来说,更高级的语言似乎会放大这个差距,C++和C#相差10倍。

总结:
归并排序是用空间换时间,而插入排序是用时间换空间。

2 comments:

  1. 做笔记,动手实验都是很好的习惯。看到你的 CPU 比我两个月前刚买的这台略慢,有点小得意。不过我基本没干啥正事,除了打游戏就是压片,你之前推荐的那个 ProjectEuler 只做到 21 题,主要还是在上班时玩。

    ReplyDelete
  2. CPU是浮云~Project Euler上的题目可以用多种语言实现,我有段时间没做那题目了,半途看了本数论,复习了线代,算是练习基本功吧。

    ReplyDelete