这几天准备.net的面试 周末手写快速排序练练手,一时兴起想测测自己写的快速排序性能如何,尤其是对比以前写过的快速排序,发现了.net性能上的一点儿有趣的事情
首先是一段高扩展性的快速排序,支持泛型,同时还使用lambda表达式使得该快速排序可以支持任何类型的排序,只要提供了排序方法即可,代码如下
public static void Sort<T>(List<T> arrayForSort, int startIndex, int endIndex, Func<T, T, bool> CompareMethod)
{
if (startIndex >= endIndex)
{
return;
}
int pointer = startIndex;
int end = endIndex;
for (int i = pointer + 1; i <= end; i++)
{
if (CompareMethod(arrayForSort[pointer], arrayForSort[i]))//把比当前锚点数小的数全部移到锚点数左边
{
var temp = arrayForSort[pointer];
arrayForSort[pointer] = arrayForSort[i];
arrayForSort[i] = temp;
pointer = i;
}
else//若当前比较的数比锚点数大 则将其与最后面的数对调
{
var temp = arrayForSort[end];
arrayForSort[end] = arrayForSort[i];
arrayForSort[i] = temp;
--i;
--end;
}
}
Sort<T>(arrayForSort, startIndex, pointer, CompareMethod);
Sort<T>(arrayForSort, pointer+1, endIndex, CompareMethod);
}然后同样的排序方法,写法也一样,但是去掉了方便扩展的泛型支持以及lambda,这样代码就只能对list<int>排序了代码如下(其实就是把参数改了下,删除了最后的CompareMethod参数)
public static void Sort(List<int> arrayForSort, int startIndex, int endIndex)
{
if (startIndex >= endIndex)
{
return;
}
int pointer = startIndex;
int end = endIndex;
for (int i = pointer + 1; i <= end; i++)
{
if (arrayForSort[pointer] > arrayForSort[i])//把比当前锚点数小的数全部移到锚点数左边
{
var temp = arrayForSort[pointer];
arrayForSort[pointer] = arrayForSort[i];
arrayForSort[i] = temp;
pointer = i;
}
else//若当前比较的数比锚点数大 则将其与后面的数对调
{
var temp = arrayForSort[end];
arrayForSort[end] = arrayForSort[i];
arrayForSort[i] = temp;
--i;
--end;
}
}
Sort(arrayForSort, startIndex, pointer);
Sort(arrayForSort, pointer + 1, endIndex);
}然后进一步简化 讲list<int>改为int[]数组,代码不用动,只需要改参数就可以了
public static void Sort(int[] arrayForSort, int startIndex, int endIndex)
{
if (startIndex >= endIndex)
{
return;
}
int pointer = startIndex;
int end = endIndex;
for (int i = pointer + 1; i <= end; i++)
{
if (arrayForSort[pointer] > arrayForSort[i])//把比当前锚点数小的数全部移到锚点数左边
{
var temp = arrayForSort[pointer];
arrayForSort[pointer] = arrayForSort[i];
arrayForSort[i] = temp;
pointer = i;
}
else//若当前比较的数比锚点数大 则将其与后面的数对调
{
var temp = arrayForSort[end];
arrayForSort[end] = arrayForSort[i];
arrayForSort[i] = temp;
--i;
--end;
}
}
Sort(arrayForSort, startIndex, pointer);
Sort(arrayForSort, pointer + 1, endIndex);
}最后是快排的另一种写法,同样是用int[]做容器(其实我已经不怎么看得懂当时是如何写的了。。。。。)
public static void SortAnotherMethod(int[] source, int startPoint, int endPoint)
{
if (startPoint < endPoint)
{
int i = partrn(source, startPoint, endPoint);
SortAnotherMethod(source, startPoint, i - 1);
SortAnotherMethod(source, i + 1, endPoint);
}
}
private static void swap(int[] source, int left, int right)
{
source[left] = source[right] + source[left];
source[right] = source[left] - source[right];
source[left] = source[left] - source[right];
}
private static int partrn(int[] source, int left, int right)
{
int pivot = source[left];
while (left < right)
{
while (left < right && source[right] >= pivot)
right--;
if (left < right)
{
swap(source, left, right);
}
while (left < right && source[left] <= pivot)
left++;
if (left < right)
{
swap(source, left, right);
}
}
source[left] = pivot;
return left;
} 最后是相对简单的调用代码。。利用stopwatch计时,这里利用快速排序对一千万个数进行排序 数字范围在一亿里面
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace QuickSort
{
class Program
{
static void Main(string[] args)
{
Random rdm = new Random();
List<int> testList = new List<int>();
List<int> testList2 = new List<int>();
int[] testArray4 = new int[10000000];
List<int> testList3 = new List<int>();
int[] testArray3 = new int[10000000];
for (int i = 0; i < 10000000; i++)
{
var number = rdm.Next(100000000);
testList.Insert(i, number);
testList2.Insert(i, number);
testList3.Insert(i, number);
testArray3[i] = number;
testArray4[i] = number;
}
var watch1 = new Stopwatch();
watch1.Start();
QuickSortMain.Sort<int>(testList, 0, testList.Count-1, (x, y) => { return x > y; });
watch1.Stop();
var customSort = watch1.ElapsedMilliseconds;
watch1.Reset();
watch1.Start();
QuickSortMain.Sort(testList2, 0, testList2.Count - 1);
watch1.Stop();
var customSort2 = watch1.ElapsedMilliseconds;
watch1.Reset();
watch1.Start();
QuickSortMain.Sort(testArray3, 0, testArray3.Length - 1);
watch1.Stop();
var customSort3 = watch1.ElapsedMilliseconds;
watch1.Reset();
watch1.Start();
QuickSortMain.SortAnotherMethod(testArray4, 0, testArray4.Length - 1);
watch1.Stop();
var customSort4 = watch1.ElapsedMilliseconds;
watch1.Reset();
watch1.Start();
testList3.Sort();
watch1.Stop();
var systemSort = watch1.ElapsedMilliseconds;
Console.WriteLine("泛型方法,涉及lambda调用 Custom1 Sort:" + customSort);//快排,比较方法用了lambda,容器为list<int>
Console.WriteLine("直接list<int>做容器 Custom2 Sort:" + customSort2);//直接用list<int>做容器
Console.WriteLine("直接int[]做容器 Custom3 Sort:" + customSort3);//容器为Int[],写法上和1,2是一样的
Console.WriteLine("直接int[]做容器的另一种写法 Custom4 Sort:" + customSort4);//容器同样为int[], 算法同样是快速排序但是写法略有不同
Console.WriteLine("C#自带排序方法 System Sort:" + systemSort);//系统自带排序
Console.ReadLine();
}
}
}
激动人心的时刻到了 让我们来看看各种调用方法在时间上面的差异
很不幸自己写的方法全部败给了.net linq自带的sort(改天可以研究研究)
这里看看自己的排序方法之间的差异
第一种 扩展性最强 泛型+lambda可以应付基本上所有排序(当然.net自带的也可以这么做 但是是使用的ICompare接口,我个人认为是直接用lambda来比较方便),时间上惨不忍睹。。。和.net linq差了一个数量级还多
第二种 去掉了lambda调用,速度立马飞升三分之一,可见lambda对性能的影响是很明显的(其实任何函数调用都影响性能,这就是为啥c++里面简短的函数编译时候可以内联)
第三种 将list<int>改为了int[],速度又提升一半,很明显数组的直接内存地址读取要比list通过地址来读取快一倍(改天试试用hashtable做容器跟int[]做对比)
第四种 还是int[]做容器,只是排序算法写法稍微做了改动,本质上还是快速排序,可以看出,和最开始的唯一不同多了几步函数调用,可见函数调用对性能的影响
第五章 .net linq自带的sort......不说啥了。。。有谁知道内部是如何实现的么?
本文探讨了.NET框架中泛型、数组和Lambda表达式在排序性能上的差异。作者通过编写不同版本的快速排序算法进行测试,发现.NET LINQ的Sort方法性能最优。实验结果显示,使用Lambda表达式会降低性能,而从List转换为数组可以显著提高排序速度。此外,调整函数调用结构也会对性能产生影响。

629

被折叠的 条评论
为什么被折叠?



