1、快速排序基本思想
快速排序时C.R.A.Hoare在1962年提出的一种划分交换排序。采用分治策略(Divide-and-ConquerMethod)
该方法的基本思想是:
(1)先从数列中取出一个数作为基准数
(2)分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边,获得一个index位置,将数组划分为左右两部分(挖坑填数+分治法)
(3)再对左右区间重复第二步,直到各区间只有一个数。(递归实现)
较难理解的地方在于第二步的区间划分,在此采用挖坑填数的方法。
举例分析:
int[] arr = {3,7,5,1,6,2,4};
0 | 1 | 2 | 3 | 4 | 5 | 6 |
3 | 7 | 5 | 1 | 6 | 2 | 4 |
(2)从high开始向左找到第一个比priv小或等于priv的数。当high=5时,小于priv,将arr[5]挖出赋值给arr[low],即arr[low]=arr[high] = 2,此时索引5的位置是坑,需要找元素来进行填充
(3)从low开始向右找到第一个比priv大或等于priv的数。当low=1时,arr[low] = 7>priv,符合条件则挖出arr[1]赋值给arr[high],即arr[high] = arr[low] = 7;此时索引low=1的位置是坑。
此时数组变成了
0 | 1 | 2 | 3 | 4 | 5 | 6 |
2 | 3 | 5 | 1 | 6 | 7 | 4 |
后面再重复上面的步骤:先从后往前找第一个小于等于基准元素的,再从前向后找第一个大于等于基准元素的。
从high开始向前找,当high=3,符合条件,将arr[3]挖出,填到上一个坑,即arr[low] =arr[1] = arr[high] = 1,此时arr[3]处为坑
从low开始向后找,当low = 2,符合条件,将arr[2]挖出,填到上一个坑即arr[3] = arr[2] = 5
此时high=3.low=2.再开始找会发现low==high,所以退出。
最后将最后一个坑arr[2]填充,用基准元素。arr[2] = priv = 3;
所得数组为:
0 | 1 | 2 | 3 | 4 | 5 | 6 |
2 | 1 | 3 | 5 | 6 | 7 | 4 |
2、快速排序实现
挖坑填数法具体实现:
(1)low数组左边界,high数组右边界,priv = arr[low]将基准数挖出形成一个坑arr[low]
(2)high由后向前找到第一个比它小的,找到后挖出此数填到前一个坑arr[low]中
(3)low由前向后找到第一个比它大的,找到后挖出此数,填到前一个坑arr[high]中
(4)再重复执行(2)、(3)步,知道low==high,将基准数priv填入到arr[low]中,返回low,low即为左右两部分的分界处。
java代码实现:
public int partition(int[] arr,int low,int high)是挖坑填数的具体实现,返回分界点的位置
public void qSort(int[] arr,int low,int high)是分治方法的具体实现,采用递归
public class quickSort {
/**
* @param arr 待排序数组
* @param low 刚开始表示排序范围的第一个元素。逐渐向右移动,找到第一个比枢纽值val大的
* @param high 刚开始表示排序范围的最后一个元素。逐渐向左移动,找到第一个比枢纽值val小的
* @return 返回分割点值所在的位置
*/
public int partition(int[] arr,int low,int high){
int val = arr[low];//设置枢纽元素为每个序列的第一个元素
while(low<high){ //要循环比较
while(low<high&&arr[high]>=val){
high--;
}
arr[low] = arr[high];
while(low<high&&arr[low]<=val){
low++;
}
arr[high] = arr[low];
}
//low = high,返回枢纽的位置
arr[low] = val;
return low;
}
public void qSort(int[] arr,int low,int high){
//递归调用
int pos;
if(low<high){
pos= partition(arr,low, high);
qSort(arr, low, pos-1);
qSort(arr, pos+1, high);
}
}
public static void main(String[] args){
int[] arr = {3,7,5,1,6,2,4};
new quickSort().qSort(arr,0,arr.length-1);
for(int i = 0;i<arr.length;i++){
System.out.print(arr[i]+" ");
}
}
/**
*快速排序 被认为在所有同数量级(平均复杂度为O(n*logn))的排序方法中,平均性能最好。
*选取枢纽元素:但是若初始记录已经基本有序,这样每次如果还选择第一个元素作为枢轴元素,则再通过枢轴划分子序列时,便会出现“一边倒”的情况,
* 此时快速排序就完全成了冒泡排序,这便是最坏的情况,时间复杂度为O(n*n)。
* 所以通常枢轴元素的选择一般基于“三者取中”的原则,即比较首元素、末元素、中间元素的值,取三者中中间大小的那个。
*快速排序的空间复杂度为O(logn)。
*/
}
3、快速排序特性
时间复杂度 O(n*logn),空间复杂度:O(logn) ,稳定性:不稳定
快速排序 被认为在所有同数量级(平均复杂度为O(n*logn))的排序方法中,平均性能最好。
选取枢纽元素:但是若初始记录已经基本有序,这样每次如果还选择第一个元素作为枢轴元素,则再通过枢轴划分子序列时,便会出现“一边倒”的情况,此时快速排序就完全成了冒泡排序,这便是最坏的情况,时间复杂度为O(n*n)。所以通常枢轴元素的选择一般基于“三者取中”的原则,即比较首元素、末元素、中间元素的值,取三者中中间大小的那个。