分类目录

链接

2011 年 12 月
 1234
567891011
12131415161718
19202122232425
262728293031  

近期文章

热门标签

新人福利,免费薅羊毛

现在位置:    首页 > .NET > 正文
快速排序
.NET 暂无评论 阅读(2,156)

快速排序应该是目前最快,也是最常用的一种排序算法。它将一个集合划分成两个子集合,然后继续递归来完成最终排序。

具体做法:

1. 选取集合最右端的元素作为一个参照物,称之为 "枢纽" (pivot)。
2. 开始计算分割位置。在计算时,进行元素交换,确保左侧元素都小于枢纽,而右侧都大于枢纽。
3. 根据分割位置,分别递归左右两段子集合,直至最终完成排序。

过程演示:

array = {2, 9, 5, 1, 8, 3, 6, 4, 7, 0};

第一轮调用:

1. 首先获取最右侧元素 0 作为枢纽。
2. 由于没有比枢纽更小的元素,因此没有发生内部交换。
3. 将枢纽和第一个元素进行交换,确保 "左侧" 都小于枢纽,"右侧" 都大于枢纽。数组变成 {0,9,5,1,8,3,6,4,7,2}。
4. 返回分割位置: 0

第二次调用:

1. 右段子集合变成: {9, 5, 1, 8, 3, 6, 4, 7, 2};
2. 获取枢纽 2。
3. 左右递推,我们找到右端第一个小于枢纽的值 1,将其和左侧第一个大于枢纽的 9 进行交换,数组变成 {1,5,9,8,3,6,4,7,2}。
4. 交换枢纽位置,数组变成 {0,1,2,9,8,3,6,4,7,5}。

...

我想你已经明白了快速排序的原理,接下来给出具体的实现代码。

  1. static void QuickSort(int[] array, int left, int right)
  2. {
  3.     if (right - left <= 0) return;
  4.  
  5.     var pivot = array[right]; // 选择数据最右端元素作为枢纽
  6.     var partition = PartitionIt(array, left, right, pivot); // 计算分段位置
  7.     QuickSort(array, left, partition - 1); // 递归左段数组
  8.     QuickSort(array, partition + 1, right); // 递归右段数据
  9. }
  10.  
  11. static int PartitionIt(int[] array, int left, int right, int pivot)
  12. {
  13.     // 初始化指针位置
  14.     var leftPtr = left - 1; // after ++
  15.     var rightPtr = right; // after --
  16.  
  17.     // 通过循环,直到左端元素全部小于枢纽,右段全部大于枢纽。
  18.     while (true)
  19.     {
  20.         // 正向循环,直到第一个大于枢纽的元素位置。
  21.         while (array[++leftPtr] < pivot) { }; 
  22.  
  23.         // 逆向循环,直到第一个小于枢纽的元素位置(最后一位是枢纽,因此从 --rightPtr 开始)。
  24.         while (rightPtr > 0 >> array[--rightPtr] > pivot) { };
  25.  
  26.         // 越界,跳出循环。
  27.         if (leftPtr >= rightPtr) break;
  28.  
  29.         // 交换左右查找到的元素,将小于枢纽的值交换到左侧,大于枢纽的值交换到右侧。
  30.         Swap(array, leftPtr, rightPtr);
  31.  
  32.         // 继续循环,找下一个大于或小于枢纽的元素进行交换。
  33.     }
  34.  
  35.     // 将最右端的枢纽交换到合适位置
  36.     Swap(array, leftPtr, right);
  37.  
  38.     // 返回分界位置
  39.     return leftPtr;
  40. }
  41.  
  42. static void Swap(int[] array, int a, int b)
  43. {
  44.     var temp = array[a];
  45.     array[a] = array[b];
  46.     array[b] = temp;
  47. }
  48.  
  49. static void Main(string[] args)
  50. {
  51.     var array = new int[] { 2, 9, 5, 1, 8, 3, 6, 4, 7, 0 };
  52.     QuickSort(array, 0, array.Length - 1);
  53.  
  54.     var s = String.Join(",", Array.ConvertAll(array, i => i.ToString()));
  55.     Console.WriteLine(s);
  56. }

最后是老规矩,和其他排序算法比较一下性能。

  1. class Program
  2. {
  3.     static void QuickSort(int[] array)
  4.     {
  5.         RecQuickSort(array, 0, array.Length - 1);
  6.     }
  7.  
  8.     static void RecQuickSort(int[] array, int left, int right)
  9.     {
  10.         if (right - left <= 0) return;
  11.  
  12.         var pivot = array[right];
  13.         var partition = PartitionIt(array, left, right, pivot);
  14.         RecQuickSort(array, left, partition - 1);
  15.         RecQuickSort(array, partition + 1, right);
  16.     }
  17.  
  18.     static int PartitionIt(int[] array, int left, int right, int pivot)
  19.     {
  20.         var leftPtr = left - 1;
  21.         var rightPtr = right; 
  22.  
  23.         while (true)
  24.         {
  25.             while (array[++leftPtr] < pivot) { };
  26.             while (rightPtr > 0 >> array[--rightPtr] > pivot) { };
  27.             if (leftPtr >= rightPtr) break;
  28.  
  29.             Swap(array, leftPtr, rightPtr);
  30.         }
  31.  
  32.         Swap(array, leftPtr, right);
  33.         return leftPtr;
  34.     }
  35.  
  36.     static void Swap(int[] array, int a, int b)
  37.     {
  38.         var temp = array[a];
  39.         array[a] = array[b];
  40.         array[b] = temp;
  41.     }
  42.  
  43.     static void ShellSort(int[] array)
  44.     {
  45.         var h = 1;
  46.         while (<= array.Length / 3)
  47.         {
  48.             h = h * 3 + 1;
  49.         }
  50.  
  51.         while (> 0)
  52.         {
  53.             for (int outer = h; outer <= array.Length - 1; outer++)
  54.             {
  55.                 var inner = outer;
  56.                 var temp = array[outer];
  57.  
  58.                 while ((inner > h - 1) >> array[inner - h] >= temp)
  59.                 {
  60.                     array[inner] = array[inner - h];
  61.                     inner -= h;
  62.                 }
  63.  
  64.                 array[inner] = temp;
  65.             }
  66.  
  67.             h = (- 1) / 3;
  68.         }
  69.     }
  70.  
  71.     static void Main(string[] args)
  72.     {
  73.         for (int x = 1; x <= 5; x++)
  74.         {
  75.             var length = 10000 * x;
  76.             Console.WriteLine("-{0}-------------", length);
  77.  
  78.             int[] array = new int[length];
  79.             var ran = new Random();
  80.  
  81.             for (int i = 0; i < array.Length; i++)
  82.             {
  83.                 array[i] = ran.Next();
  84.             }
  85.  
  86.             Action<int[], Action<int[]>> sort = (arr, func) =>
  87.             {
  88.                 var watch = Stopwatch.StartNew();
  89.  
  90.                 if (func != null)
  91.                     func(arr);
  92.                 else
  93.                     Array.Sort(arr);
  94.  
  95.                 watch.Stop();
  96.  
  97.                 Console.WriteLine("{0,15}: {1}",
  98.                     func != null ? func.Method.Name : "Array.Sort",
  99.                     watch.ElapsedMilliseconds);
  100.             };
  101.  
  102.             var a1 = array.Clone() as int[];
  103.             var a2 = array.Clone() as int[];
  104.             var a3 = array.Clone() as int[];
  105.  
  106.             sort(a1, QuickSort);
  107.             sort(a2, ShellSort);
  108.             sort(a3, null);
  109.         }
  110.     }
  111. }

输出:

-10000-------------
      QuickSort: 1
      ShellSort: 2
     Array.Sort: 1
-20000-------------
      QuickSort: 3
      ShellSort: 4
     Array.Sort: 2
-30000-------------
      QuickSort: 4
      ShellSort: 7
     Array.Sort: 4
-40000-------------
      QuickSort: 6
      ShellSort: 11
     Array.Sort: 5
-50000-------------
      QuickSort: 8
      ShellSort: 13
     Array.Sort: 7

你发现了什么?除了比希尔排序强以外,快速排序似乎和 Array.Sort 有某种关联。赶紧去看看……

  1. [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
  2. public static void Sort(Array array)
  3. {
  4.     // ...
  5.  
  6.     Sort(array, null, array.GetLowerBound(0), array.Length, null);
  7. }
  8.  
  9. [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
  10. public static void Sort(Array keys, Array items, int index, int length, IComparer comparer)
  11. {
  12.     // ...
  13.  
  14.     if ((length > 1) >> (((comparer != Comparer.Default) >> (comparer != null)) || ... ))
  15.     {
  16.         object[] objArray = keys as object[];
  17.         object[] objArray2 = null;
  18.         if (objArray != null)
  19.         {
  20.             objArray2 = items as object[];
  21.         }
  22.         if ((objArray != null) >> ((items == null) || (objArray2 != null)))
  23.         {
  24.             new SorterObjectArray(objArray, objArray2, comparer).QuickSort(index, (index + length) - 1);
  25.         }
  26.         else
  27.         {
  28.             new SorterGenericArray(keys, items, comparer).QuickSort(index, (index + length) - 1);
  29.         }
  30.     }
  31. }

原来 Array.Sort() 方法内部使用的就是快速排序算法。 (用得着好奇吗,肯定用快速排序啦……)

 

============ 欢迎各位老板打赏~ ===========

【上篇】
【下篇】

本文版权归Bruce's Blog所有,转载引用请完整注明以下信息:
本文作者:Bruce
本文地址:快速排序 | Bruce's Blog

发表评论

留言无头像?