快速排序算法

一、什么是快速排序算法?算法

快速排序是由东尼·霍尔所发展的一种排序算法,犹如他的名字同样,速度快,效率高,也是实际数组

中最经常使用的一种算法,被称为20实际对世界影响最大的算法之一。函数

基本思想:性能

1): 从序列中挑出一个元素做为"基准"元素,通常是该序列的第一个元素或者是最后一个元素。学习

2): 把序列分红2个部分,其数值大于"基准"元素的元素放在"基准"元素的左边,否在放在"基准"元测试

素的右边,此时"基准"元素所在的位置就是正确的排序位置,这个过程被称为 partition(分区)。优化

3): 递归将"基准"元素左边的序列和"基准"元素右边的序列进行partition操做。动画

 

二、算法的演示ui

 这个就是待排序的数组序列,第一个元素做为"基准"元素spa

 给"基准"元素找到合适的位置,将比"基准"元素小的元素放在其左边,不然放在其右边

  至此这个序列就成了这样了,这个过程成为partition

下面来看看partition的具体实现过程:

 将"基准"元素用v表示,使用i做为遍历序列的索引值,j的位置

                                                        表示>v部分和<v部分的分界位置(也就是最后一个小于v的元

                                                        素所在位置)。

     

 若是此时i指向的元素大于v,这个好处理,直接将i++便可,

                                                        也就表示大于v的元素多了一个

 

 若是此时i指向的元素小于v,那么须要将i指向的元素与大于

                                                         v序列的第一个元素交换位置,即swap(arr[i], arr[j+1]),

                                                         而后再将i++,再将j++便可,表示小于v的元素多了一个。

                                                         以下图所示

 进行swap(arr[i], arr[j+1])

 

  j++

 

 i++

 

 由此可知,当遍历完成以后,就会出现这样的效果,而后我

                                                          们只需将元素v与j指向的元素交换位置便可

 

 此时就出现了小于"基准"元素的元素在其左边,大于"基准"

                                                        元素的元素在其右边的分布状况

 

三、算法实现的动画效果

 

四、算法的实现(基于C++)

 1 /*********************************** 随机化快速排序算法实现 ****************************************/
 2 // 对arr[left....right]进行partition操做
 3 // 返回p,使得arr[left, p-1] < arr[p],arr[p+1, right] > arr[p]
 4 template<typename T>
 5 int __partition (T arr[], int left, int right)
 6 {
 7     T v = arr[left];          // 将第一个元素做为"基准"元素
 8     int j = left;             // 将<v部分的分界点位置j初始化为本序列中的第一个元素位置(也就是<v部分的最后一个元素位置)
 9 
10     // 将遍历序列的索引值i初始为第二个元素位置
11     for (int i = left + 1; i <= right; i++) {
12         if (arr[i] < v) {     // 若是i指向的元素<v,那么将此元素与>v部分的第一个元素交换位置,而后j++,表示<v的元素又多了一个
13             j++;              
14             std::swap(arr[j], arr[i]);  // 这里采用了另外一种那写法,由于j++以后指向的就是>v部分的第一个元素,交换位置以后其实相似于将>v的部分总体往右边移动了一个位置
15         }
16     }
17 
18     std::swap(arr[left], arr[j]);   // 遍历完成以后只须要将"基准"元素(也就是第一个元素)与当前j指向的位置交换位置便可
19     return j;                       // 由于"基准"元素并不属于<v的部分,因此交换以后此时j指向的元素就是"基准"元素
20 }
21 
22 // 对arr[left...right]范围内数组序列进行快速排序操做
23 template<typename T>
24 void __quickSort (T arr[], int left, int right)
25 {
26     int p = __partition<T>(arr, left, right);  // 对arr[left...right]区间元素进行partition操做,找到"基准"元素
27     __quickSort<T>(arr, left, p - 1);          // 对基准元素以前的序列递归调用__quickSort函数
28     __quickSort<T>(arr, p + 1, right);         // 对基准元素以后的序列递归调用__quickSort函数
29 }
30 
31 template<typename T>
32 void quickSort (T arr[], int count)
33 {
34     __quickSort<T>(arr, 0, count - 1);   // 调用__quickSort函数进行快速排序
35 }
36 /********************************************************************************************/ 

 

五、算法性能测试

与前面的2种归并排序算法相比较

测试数据量100000:

 

测试数据量1000000:

从上面能够知道,很明显快速排序要比归并排序快,这仍是在快速排序没有优化的状况下。不一样的

电脑因为配置不同,可能获得的测试结果是不一样的。

 

六、快速排序算法的优化

(1)第一种优化

在前面的学习过程当中已经说到了,那就是对于几乎全部的高级算法均可以使用的一种优化方法,

当递归到元素个数很小时可使用直接插入排序。

(2)第二种优化(随机化快速排序算法)

咱们先来看一个例子,当待排序的数组序列接近为一个有序序列的时候,归并排序和快速排序的性能测试

测试数据量500000:

经过上面能够知道,当序列接近为有序状态的时候,快速排序慢得要死,这是为何呢?请看下面的分析:

对于快速排序,由于咱们是将第一个元素做为"基准"元素,但因为序列基本接近为有序,从而致使每个

"基准"元素的左边没有一个元素,而所有

在他的右边,这样就会致使快速排序算法的高度为n,将退化为O(n^2)级别。

 

那么这种状况的解决办法就是: 尽量的别让第一个元素成为"基准"元素,而最好使用中间位置的元素成为

"基准"元素,那如何作到这点呢?

解决办法就是"基准"元素随机产生,而不指定。请看下面的代码:

 1 /*********************************** 随机化快速排序算法实现 ****************************************/
 2 // 对arr[left....right]进行partition操做
 3 // 返回p,使得arr[left, p-1] < arr[p],arr[p+1, right] > arr[p]
 4 template<typename T>
 5 int __partition (T arr[], int left, int right)
 6 {
 7     std::swap(arr[left], arr[std::rand()%(right-left+1)+left]);  // 随机产生"基准"元素所在位置,并与第一个元素交换位置
 8 
 9     T v = arr[left];          // 将第一个元素做为"基准"元素
10     int j = left;             // 将<v部分的分界点位置j初始化为本序列中的第一个元素位置(也就是<v部分的最后一个元素位置)
11 
12     // 将遍历序列的索引值i初始为第二个元素位置
13     for (int i = left + 1; i <= right; i++) {
14         if (arr[i] < v) {     // 若是i指向的元素<v,那么将此元素与>v部分的第一个元素交换位置,而后j++,表示<v的元素又多了一个
15             j++;              
16             std::swap(arr[j], arr[i]);  // 这里采用了另外一种那写法,由于j++以后指向的就是>v部分的第一个元素,交换位置以后其实相似于将>v的部分总体往右边移动了一个位置
17         }
18     }
19 
20     std::swap(arr[left], arr[j]);   // 遍历完成以后只须要将"基准"元素(也就是第一个元素)与当前j指向的位置交换位置便可
21     return j;                       // 由于"基准"元素并不属于<v的部分,因此交换以后此时j指向的元素就是"基准"元素
22 }
23 
24 // 对arr[left...right]范围内数组序列进行快速排序操做
25 template<typename T>
26 void __quickSort (T arr[], int left, int right)
27 {
28     if (right - left <= 40) {      // 对归并到序列中元素个数较小时采用插入排序算法
29         __insertSortMG<T>(arr, left, right);
30         return;
31     }
32 
33     int p = __partition<T>(arr, left, right);  // 对arr[left...right]区间元素进行partition操做,找到"基准"元素
34     __quickSort<T>(arr, left, p - 1);          // 对基准元素以前的序列递归调用__quickSort函数
35     __quickSort<T>(arr, p + 1, right);         // 对基准元素以后的序列递归调用__quickSort函数
36 }
37 
38 template<typename T>
39 void quickSort (T arr[], int count)
40 {
41     std::srand(std::time(NULL));         // 种下随机种子
42     __quickSort<T>(arr, 0, count - 1);   // 调用__quickSort函数进行快速排序
43 }
44 /********************************************************************************************/

此次的改变在于: 在quickSort函数中种下了随机种子,而后在__partition函数中使用rand函数来产生随机的位置,

将此位置的元素做为"基准"元素,从而能够避免使用第一个元素做为"基准"。使用了随机化快速排序以后,虽然在排

序通常的序列时会比以前的快速排序算法要慢,可是以前的快速排序算法对于一个近乎有序的序列时就不行了,而随

机化快速排序就可以很好的解决这样的问题,因此随机化快速排序就可以兼顾这样两种不一样的状况,并且还可以快速

的对序列进行排序。来看看与归并排序算法之间的性能比较:

通常序列数据量1000000:

 

近乎有序的序列数据量1000000:

从上面的测试获得的结果能够看出来,对于通常的序列使用随机化快速排序要比归并排序快,而对于近乎有序的序

列明显归并排序要快,这是归并排序的一个优点,以前说过;可是实际中出现近乎有序序列的几率是很低很低的,

因此彻底能够认为随机化快速排序在整体上比归并排序快。

相关文章
相关标签/搜索