快速排序算法
什么是快速排序:
快速排序是对冒泡排序的优化,优化的着眼点是:在冒泡排序中,记录的比较和移动是在相邻的位置进行的,记录每次交换后只能向后移动一个位置,因而总的比较和移动次数比较多。在快速排序中,记录和比较是从两端向中间进行的,关键码较大的记录一次就能从前面移到后面,记录移动距离比较远,从而减少了总的比较移动次数
快速排序基本思想:
首先选择一个轴值(比较的基准),将待排序记录划分成独立的两部分,左侧记录的关键码均小于或等于轴值,右侧记录的关键码均大于或等于轴值,然后分别对这两部分重复上述过程,知道整个序列有序为止。
伪代码:
1:将i和j分别指向待划分区间的最左侧记录和最右侧的记录位置。
2:重复下述过程,知道i==j
2.1:右侧扫描,直到记录j的关键码小于轴值记录的关键码。
2.2:如果存在划分区间,则将r[j]和r[i]交换,并i++。
2.3:左侧扫描,直到记录i的关键码大于轴值记录的关键码。
2.4:如果存在划分区间,则将r[i]和r[j]交换,并j--。
3:退出循环,说明i和j指向了轴值记录所在位置,返回该位置。
一次划分:
代码实现:
package com.renyou.算法工具包;
public class Util {
/**
* 快速排序
*
* @param array
* 待快速排序数组
* @param first
* 待快速排序序列的最左边所在位置
* @param end
* 待快速排序序列的最右边所在位置
*/
public static int[] sort(int[] array, int first, int end) {
int pivot = 0;// 轴值记录所在位置
if (first < end) {// 区间长度大于一才会执行一次划分,否则递归结束
pivot = partition(array, first, end);// 进行一次划分
sort(array, first, pivot - 1);// 递归地对左序列进行快速排序
sort(array, pivot + 1, end);// 递归地对右序列进行快速排序
}
return array;
}
/**
* 快速排序一次划分算法
*
* @param array
* 待快速排序数组
* @param first
* 待快速排序序列的最左边所在位置
* @param end
* 待快速排序序列的最右边所在位置
* @return 每次划分序列轴值最后记录所在位置
*/
public static int partition(int[] array, int first, int end) {
while (first < end) {
while (first < end && array[first] <= array[end]) {// 右侧扫描
end--;
}
if (first < end) {
swap(array, first, end);
first++;
}
while (first < end && array[first] <= array[end]) {// 左侧扫描
first++;
}
if (first < end) {
swap(array, end, first);
end--;
}
}
return first;
}
/**
*
* @param array
* 待交换数组
* @param i
* 待交换数组的一个索引
* @param j
* 待交换数组的另一个索引
*/
public static void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
复杂度分析:
最好情况:每次划分对一个记录定位后,该记录左侧子序列和右侧子序列长度一样。在具有n个记录的序列中,扫描一遍需要O(n)的时间,那么每次划分后正好把待划分区间分成长度相等的两个子序列。则有:
T(n)<=2T(n/2)+n
<=2(2T(n/4)+n/2)+n=4T(n/4)+2n
<=4(2T(n/8)+n/4)+2n=8T(n/8)+3n
:
:
<=nT(1)+nLog2(n)=O(nLog2(n))
最坏情况:待排序序列正好是正序或者逆序列,每次划分只得到比上次少一个的子序列,另一个子序列为空。1/2n(n-1)=O(n*n)
平均情况:O(nLog2(n))
总结:
快速排序是一种不稳定的排序算法,适用于待排序记录个数很大且原始记录数据随机排列的情况,快速排序的平均性能是所有内排序算法最好的一种。