堆排序的最坏时间复杂度是O(nlogn)
,而快速排序的时间复杂度为O(n^2)
。
但实际证据表明,快速排序更优秀。为什么呢?
主要因素之一是快速排序具有更好的本地性——下一个要访问的元素通常在刚刚查看过的元素附近,相比之下,堆排序跳动较多。由于接近的元素可能会被缓存在一起,因此快速排序往往更快。
然而,快速排序的最坏情况性能明显差于堆排序。由于某些关键应用程序需要保证速度性能,对于这些情况,堆排序是正确的选择。
static void Main(string[] args)
{
int[] arrToSort = new int[100000000];
var r = new Random();
for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
Console.WriteLine("Press q to quick sort, s to Array.Sort");
while (true)
{
var k = Console.ReadKey(true);
if (k.KeyChar == 'q')
{
// quick sort
Console.WriteLine("Beg quick sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
QuickSort(arrToSort, 0, arrToSort.Length - 1);
Console.WriteLine("End quick sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
}
else if (k.KeyChar == 's')
{
Console.WriteLine("Beg Array.Sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
Array.Sort(arrToSort);
Console.WriteLine("End Array.Sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
}
}
}
static public void QuickSort(int[] arr, int left, int right)
{
int begin = left
, end = right
, pivot
// get middle element pivot
//= arr[(left + right) / 2]
;
//improved pivot
int middle = (left + right) / 2;
int
LM = arr[left].CompareTo(arr[middle])
, MR = arr[middle].CompareTo(arr[right])
, LR = arr[left].CompareTo(arr[right])
;
if (-1 * LM == LR)
pivot = arr[left];
else
if (MR == -1 * LR)
pivot = arr[right];
else
pivot = arr[middle];
do
{
while (arr[left] < pivot) left++;
while (arr[right] > pivot) right--;
if(left <= right)
{
int temp = arr[right];
arr[right] = arr[left];
arr[left] = temp;
left++;
right--;
}
} while (left <= right);
if (left < end) QuickSort(arr, left, end);
if (begin < right) QuickSort(arr, begin, right);
}
这里有几个解释:
http://www.cs.auckland.ac.nz/software/AlgAnim/qsort3.html
http://users.aims.ac.za/~mackay/sorting/sorting.html
实际上,尽管快速排序的最坏情况是O(n^2),但平均而言它的表现会更好。 :-)
大O符号表示排序n个项目所需的时间受到函数c*n*log(n)的上限约束,其中c是一些未指定的常数因子。对于快速排序和堆排序,常量c没有理由相同。因此,真正的问题是:为什么您期望它们同样快呢?
实践中,快速排序总是比堆排序要快一些,但最近差别变得更大了,因为如前所述,内存访问的局部性对执行速度非常重要。
平均情况下的复杂度,以及您可以采取简单步骤来最小化Quicksort中最坏情况下的复杂度的事实(例如选择三个元素的中位数作为枢轴而不是单个选定位置)。