博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
算法总结系列之二: 快速排序(QuickSort)
阅读量:4677 次
发布时间:2019-06-09

本文共 2863 字,大约阅读时间需要 9 分钟。

快速排序是现有的比较排序算法中,效率最好的一种排序算法.

所谓比较排序,是对排序对象的值的比较, 它不特定于排序对象本身的额外特征或排序对象因特定的数据结构而获得的额外条件.

快速排序主要体现分而治之的思想, 主要做法是不断的"选取基准点 - 划分子序列",直至子序列长度为1.

假定数组A[p.......r] - p,r都是数组下标.

 

算法的C#实现:

public void QuickSort( int[] A, int begin, int end )
{
if ( begin < end )
{
//Procedure Partition split the array into 2 subarry.
int q = Partition( A, begin ,end );
QuickSort( A, begin, q-1 );
QuickSort( A, q, end );
}
}
 
// 1 sort to get lower partition - judge point - larger partition
public int Partition( int[] A, int bengin, int end )
{
// take the last element of array for judge point
int judgeValue = A[ end ];
 
// i is the bound of elements lower than judgeValue.
int i = begin -1;
 
for( int j = begin ; j < end ; j ++ )
{
if( A[j] <= judgeValue )
{
//expand the lower bound
i += 1;
 
//exchange A[i] with current element.
int tmp = A[i];
A[i] = A[j];
A[j] = tmp;
}
}
 
// exchange the judge element with the beginner of larger elements
int tmp = A[ i+1 ];
A[i+1] = A[end];
A[end] = tmp; return i+1;
}

 

以上Partition函数的一次排序的过程如下:

原数组: 2 1 7 8 3 4 5

1.选中最后一个元素 5 为判断标准, 此时 bengin = 0 ; end = 6; i = -1; j 从0 到5

2. j = 0 : 判断A[0]小于5, i此时为0, 交换A[0]自身.

    2 1 7 8 3 4 5

3. j = 1 : 判断A[1]小于5, i此时为1, 交换A[1]自身.

    2 1 7 8 3 4 5

4. j = 2 : 判断A[2]大于5, i此时为1, 没有交换行为.

    2 1 7 8 3 4 5

5. j = 3 : 判断A[3]大于5, i此时为1, 没有交换行为.

    2 1 7 8 3 4 5

6. j = 4 : 判断A[4]小于5, i此时为2, 交换A[2]和A[4], 即交换7和3;

    2 1 3 8 7 4 5

7. j = 5 : 判断A[5]小于5, i此时为3, 交换A[3]和A[5], 即交换8和4;

    2 1 3 4 7 8 5

8. 完成循环, 交换A[3+1]和A[6];

    2 1 3 4 5 8 7

此时数组分成 {2, 1, 3, 4}, {5}, {8,7} 三个部分.

 

Note: 由Step 2, 3 可以看出, 当开始的元素小于基准值时, 自身被迫与自身交换. 可以做简化至 判断 j 和 i当前是否相等 决定是否进行交换. 不过这事实上不会影响排序的效率.

快速排序的平均时间复杂度是O(nlgn), 最坏可能达到O(n2)

 

上述的Partition算法是我们比较常用的. 它采用每个序列的最后一个元素来作为划分基准. 没有任何理由判定划分以后的两个子序列是否平衡(长度是否相差不多或者相等). 理论上来说, 越平衡的划分, 快速排序的时间复杂度就越无限逼近O(nlgn). 所以优化快速排序的基本方向,就是追求更加平衡的partition函数.

另外一种我们教材常见的partition函数由Hoare给出, C#实现如下:

public void Partition( int[] A, int begin, int end )
{
 
int judgeValue = A[begin];
int i = begin - 1 ;
int j = end + 1 ;
 
while(true)
{
do
{
j --;
}while( A[j] > judgeValue )
do
{
i ++;
}while( A[i] < judgeValue )
 
if ( i < j )
{
int tmp = A[i];
A[i] = A[j];
A[j] = tmp;
}
else
return j;
}
}
 

以上Partition函数的一次排序的过程如下:

原数组: 3 1 7 8 2 4 5 (为了示例方便,调整了一下上面的示例数组)

1.选中第一个元素 3 为判断标准, 此时 bengin = 0 ; end = 6; i = -1; j = 7

2. j从最后向前寻找第一个不大于3的数, 找到A[4] = 2; 此时j = 4;

    i从最前向后寻找第一个不小于3的数, 找到A[2] = 7; 此时i = 2;

3. 交换A[4]和A[2], 数组成为 3 1 2 8 7 4 5

4. j继续向前寻找不大于3的数, 找到A[2]= 1; 此时j = 2;

    i继续向后寻找不小于3的数,找到A[3] = 8; 此时i = 3;

5. 此时i > j, 数组被分为3, 1,2 和8, 7, 4, 5两部分.返回2.

 

实际上,在寻求优化分区函数的过程中,常使用随即函数来在当前数组中随机取得一个判断基准点. 这样可以使快速排序更好的接近平均效率.

一个可能的版本是:

public void RandomPartition( int[] A, int begin , int end )
{
int i = Random( begin , end ) ;
 
// exchange the random element with the last element.
int tmp = A [i];
A[i] = A[end];
A[end] = tmp;
 
return Partition( A, begin, end );
}

转载于:https://www.cnblogs.com/sun/archive/2008/06/20/1227005.html

你可能感兴趣的文章
匈牙利算法求解任务分配问题
查看>>
开源协议的比较
查看>>
1115 Counting Nodes in a BST (30 分)建立二叉搜索树,求每层结点数目
查看>>
Linux修改hostname与免密码登录
查看>>
node全局对象 文件系统
查看>>
力扣——找数左下角的值
查看>>
201521123042 《Java程序设计》 第10周学习总结
查看>>
JQuery Easyui引入easyui-lang-zh_CN.js后出现乱码的问题解决方法
查看>>
cookie domain port
查看>>
springboot中starters 提供的常用的依赖
查看>>
第二章
查看>>
实现表单元素与文字的居中对齐
查看>>
vuex
查看>>
swift上传图片
查看>>
struts2常量的配置
查看>>
【设计模式】原型模式
查看>>
【监控】WebServer入库与缓存更新代码优化小计
查看>>
[洛谷P5174]圆点
查看>>
Spark BlockManager的通信及内存占用分析(源码阅读九)
查看>>
AJAX 局部刷新 GridView 进行数据绑定
查看>>