排序---快速排序

一、概念

快速排序由C.A.R.Hoare在1962年提出,是冒泡排序的一种改进。其基本思想为:通过一趟排序将待排序数据分割成独立的两部分,其中一部分的所有值都比另一部分的所有值都小,然后再对分割的两部分分别进行快速排序,整个过程可以递归进行,最终所有数据变为有序序列。

二、算法要点

假设待排序数组为a[0], a[1],…a[n-1],快速排序步骤以下:

1、初始化两个变量i、j,刚开始i = 1,j=n-1。

2、将第一个元素a[0]作为基准数。

3、从i开始向后搜索,找到第一个大于基准数的元素a[i]。

4、从j开始向前搜索,找到第一个小于等于基准数的元素a[j]。

5、将a[i]与a[j]互换。

6、重复3到5步骤,直到i = j + 1,此时,j指向的元素是最后一个(从左边算起)小于等于中轴的元素,然后将a[0]与a[j]对换。

7、序列被基准数分割成两个分区,前面分区全部小于等于基准数,后面分区全部大于基准数。

8、递归对分区子序列进行快速排序,最终完成整个排序工作。

每趟快速排序的核心工作是:

选一个元素作为基准数,然后将所有比它小的数都放到它前面,大于它的数都放在它后面。

三、算法实现

实现一:

 1 using System;
 2 using System.Collections.Generic;
 3 
 4 namespace QuickSort
 5 {
 6     class Program
 7     {
 8         static void Main(string[] args)
 9         {
10             QuickSortUnitTest(13);
11 
12         }
13         private static void QuickSort(List<int>dataList, int left, int right)
14         {
15             if(left < right)//递归的边界条件,当left等于right时,元素个数为1个
16                 {
17                 int pivot = dataList[left];//最左边的元素作为中轴
18                 int i = left + 1;
19                 int j = right;
20                 int tmp = 0;
21                 //当i == j时,i和j同时指向的元素还没有与中轴元素判断,
22                 //小于等于中轴元素,i++,大于中轴元素j--,
23                 //当循环结束时,一定有i = j + 1, 且i指向的元素大于中轴,j指向的元素小于等于中轴
24                 while (i <= j)
25                 {
26                     while(i <= j && dataList[i] <= pivot)//从前向后找到第一个大于pivot的元素
27                     {
28                         i++;
29 
30                     }
31                     while(i <= j && dataList[j] > pivot)
32                     {
33                         j--;
34 
35                     }
36                     if(i < j)
37                     {
38                         tmp = dataList[i];
39                         dataList[i] = dataList[j];
40                         dataList[j] = tmp;
41                     }                    
42                 }
43                 //当循环结束时,j指向的元素是最后一个(从左边算起)小于等于中轴的元素
44                 //将中轴元素和j所指的元素互换
45                 tmp = dataList[left];
46                 dataList[left] = dataList[j];
47                 dataList[j] = tmp;
48                 Console.WriteLine();
49                 foreach (var item in dataList)
50                 {
51                     Console.Write($" {item} ");
52                 }
53                 Console.WriteLine();
54                 QuickSort(dataList, left, j - 1);
55                 QuickSort(dataList, j + 1  , right);
56             }
57         }
58         private static void QuickSortUnitTest(int loopNum)
59         {
60             Random random = new Random();
61             List<int> dataList = new List<int>();
62             for (int i = 0; i < loopNum; i++)
63             {
64                 dataList.Add(random.Next(100));
65             }
66             Console.Write("Original data:");
67             foreach(var item in dataList)
68             {
69                 Console.Write($" {item} ");
70             }
71 
72             QuickSort(dataList, 0, dataList.Count - 1);
73             Console.Write("Quick sorted data:");
74 
75             foreach (var item in dataList)
76             {
77                 Console.Write($" {item} ");
78             }
79             Console.WriteLine();
80         }
81     }
82 }

 实现二:

基本思想:“挖坑填坑”

使用两个变量i和j分别指向最左边和最右边的元素,我们将首元素作为中轴,并复制到变量pivot中,这时我们可以将首元素i所在的位置看成一个坑,我们从j的位置从右向左扫描,找一个小于等于中轴

的元素A[j],来填补A[i]这个坑,填补完成后,拿去填坑的元素所在的位置j又可以看做一个坑,这时我们在以i的位置从前往后找一个大于中轴的元素来填补A[j]这个新的坑,如此往复,直到

i和j相遇(i == j,此时i和j指向同一个坑)。

最后我们将中轴元素放到这个坑中。然后,我们按照相同的方法分别对左右两个数组执行相同的操作。

private static void QuickSort2(List<int>dataList, int left, int right)
        {
            if(left < right)
            {
                //最左边的元素作为中轴复制到pivot,这时最左边的元素可以看做一个坑
                int pivot = dataList[left];
                //注意这里 i = L,而不是 i = L+1, 因为i代表坑的位置,当前坑的位置位于最左边
                int i = left;
                int j = right;
                while(i < j)
                {
                    //下面面两个循环的位置不能颠倒,因为第一次坑的位置在最左边
                    while (i < j && dataList[j] > pivot)
                    {
                        j--;
                    }
                    //填A[i]这个坑,填完后A[j]是个坑

                    //注意不能是A[i++] = A[j],当因i==j时跳出上面的循环时

                    //坑为i和j共同指向的位置,执行A[i++] = A[j],会导致i比j大1,

                    //但此时i并不能表示坑的位置 
                    dataList[i] = dataList[j];
                    while(i < j && dataList[i] <= pivot)
                    {
                        i++;
                    }
                    dataList[j] = dataList[i];
                }
                //循环结束后i和j相等,都指向坑的位置,将中轴填入到这个位置
                dataList[i] = pivot;
                QuickSort2(dataList, left, i - 1);
                QuickSort2(dataList, i + 1, right);
               

            }
        }

 更多方法请参考:

https://www.cnblogs.com/nullzx/p/5880191.html

posted on 2019-02-14 15:52  缘惜  阅读(723)  评论(0编辑  收藏  举报