本文主要讲解排序算法中的快速排序 算法,希望你们会喜欢。
通过步骤2的方式,最终达到整个序列有序的目的
待排序序列 = {50,10,90,30,70,40,80,60,20}
a. 在待排序 序列中选择1个基准数据元素(此处选第1个)
b. 分别对比 高位元素、低位元素 与 基准元素,具体规则如下:
具体对比过程如下:
Partition()
将序列分割成2个独立子序列(高、低)public class QuickSort {
/**
* 参数说明:
* @param srcArray = 需排序的数组序列
* @param low = 数组第1个元素下标
* @param high = 数组最后1个元素下标
*/
public static void quickSort(int[] srcArray, int low, int high) {
if (low < high) {
// 1. 将待排序列 根据所选的枢纽位置,分割成独立的2个子序列
// 最终返回的是枢纽位置
int privot = Partition(srcArray, low, high);
// 2. 分别对这2个子序列 进行排序
// a. 通过递归 对低字表进行排序
quickSort(srcArray, low, privot - 1);
// b. 通过递归 对高字表进行排序
quickSort(srcArray, privot + 1, high);
}
}
分区函数Partition()
对于快速排序算法来说非常重要,下面将详细讲解
/**
* 参数说明:
* @param srcArray = 需排序的数组序列
* @param low = 数组第1个元素下标
* @param high = 数组最后1个元素下标
*/
public static int Partition(int[] srcArray, int low, int high) {
// 1. 将子表的第1个个记录作为枢纽
int tmp = srcArray[low];
while (low < high) {
// 2. 比较高位元素 & 枢纽元素
while (low < high && srcArray[high] >= tmp) {
high--;
}
int temp = srcArray[low];
srcArray[low] = srcArray[high];
srcArray[high] = temp;
// 3. 比较低位元素 & 枢纽元素
while (low < high && srcArray[low] <= tmp) {
low++;
}
int temp1 = srcArray[high];
srcArray[high] = srcArray[low];
srcArray[low] = temp1;
}
// 4. 最终低位、高位都会指向枢纽位置,返回
return low;
}
/**
* 执行 快速排序
*/
public static void main(String[] args) {
// 定义待排序数列
int[] src = new int[]{ 50, 10, 90, 30, 70, 40, 80, 60, 20 };
// 输出结果
quickSort(src,0,src.length-1);
// 输出 排序后的序列
for (int a = 0; a < src.length; a++) {
System.out.println(src[a]);
}
}
}
10
20
30
40
50
60
70
80
90
Demo
地址
Carson_Ho的Github地址:快速排序下面,将演示 三数取中法 的代码实现。
主要修改处:在分区函数Partition()
中将枢纽的选择方式改为三数取中,具体原理是:
/**
* 参数说明:
* @param srcArray = 需排序的数组序列
* @param low = 数组第1个元素下标
* @param high = 数组最后1个元素下标
*/
public static int Partition(int[] srcArray, int low, int high) {
/**
* 三数取中的方式
*/
// 1. 找出中间元素
// 不是使用(low+high)/2的原因:容易溢出
// 右移1位 = 除以2,但右移的运算速度更快
int m = low + ( (high-low)>>1 );
// 2. 比较左、右端数据元素,保证左端较小
// 若左>右,就交换位置
if(srcArray[low]>srcArray[high]) {
int temp = srcArray[low];
srcArray[low] = srcArray[high];
srcArray[high] = temp;
}
// 3. 比较中、右端数据元素,保证中端较小
// 若中>右,就交换位置
if(srcArray[m]>srcArray[high]) {
int temp1 = srcArray[m];
srcArray[m] = srcArray[high];
srcArray[high] = temp1;
}
// 4. 比较中、左端数据元素,保证中端较小
if(srcArray[m]>srcArray[low]) {
// 若中>左,就交换位置
int temp2 = srcArray[m];
srcArray[m] = srcArray[low];
srcArray[low] = temp2;
}
// 此时,最低位 = srcArray[low] = 三数的中间数(即 最低位、最高位 & 中间数的中间值)
// 将上述值作为枢纽
int tmp = srcArray[low];
System.out.println("枢轴位置 =" + srcArray[low]);
/**
* 下面代码类似未优化前(即,基础实现)
*/
while (low < high) {
// 比较高位元素 & 枢纽元素
while (low < high && srcArray[high] >= tmp) {
high--;
}
int temp = srcArray[low];
srcArray[low] = srcArray[high];
srcArray[high] = temp;
// 比较低位元素 & 枢纽元素
while (low < high && srcArray[low] <= tmp) {
low++;
}
int temp1 = srcArray[high];
srcArray[high] = srcArray[low];
srcArray[low] = temp1;
}
// 最终低位、高位都会指向枢纽位置,返回
return low;
}
public class QuickSort {
/**
* 快速排序算法实现(基础实现)
* 参数说明:
* @param srcArray = 需排序的数组序列
* @param low = 数组第1个元素下标
* @param high = 数组最后1个元素下标
*/
public static void quickSort(int[] srcArray, int low, int high) {
if (low < high) {
// 1. 将待排序列 根据所选的枢纽位置,分割成独立的2个子序列
// 最终返回的是枢纽位置(主要优化在取取枢纽值里)
int privot = Partition(srcArray, low, high);
// 2. 分别对这2个子序列 进行排序
// a. 通过递归 对低字表进行排序
quickSort(srcArray, low, privot - 1);
// b. 通过递归 对高字表进行排序
quickSort(srcArray, privot + 1, high);
}
}
/**
* 作用:将待排序列 根据所选的枢纽位置,分割成独立的2个子序列(优化 = 选取枢轴 = 三数取中)
* 返回值:所选的枢纽位置
* 参数说明:
* @param srcArray = 需排序的数组序列
* @param low = 数组第1个元素下标
* @param high = 数组最后1个元素下标
*/
public static int Partition(int[] srcArray, int low, int high) {
/**
* 三数取中的方式
*/
// 1. 找出中间元素
// 不是使用(low+high)/2的原因:容易溢出
// 右移1位 = 除以2,但右移的运算速度更快
int m = low + ( (high-low)>>1 );
// 2. 比较左、右端数据元素,保证左端较小
// 若左>右,就交换位置
if(srcArray[low]>srcArray[high]) {
int temp = srcArray[low];
srcArray[low] = srcArray[high];
srcArray[high] = temp;
}
// 3. 比较中、右端数据元素,保证中端较小
// 若中>右,就交换位置
if(srcArray[m]>srcArray[high]) {
int temp1 = srcArray[m];
srcArray[m] = srcArray[high];
srcArray[high] = temp1;
}
// 4. 比较中、左端数据元素,保证中端较小
if(srcArray[m]>srcArray[low]) {
// 若中>左,就交换位置
int temp2 = srcArray[m];
srcArray[m] = srcArray[low];
srcArray[low] = temp2;
}
// 此时,最低位 = srcArray[low] = 三数的中间数(即 最低位、最高位 & 中间数的中间值)
// 将上述值作为枢纽
int tmp = srcArray[low];
System.out.println("枢轴位置 =" + srcArray[low]);
/**
* 下面代码类似未优化前(即,基础实现)
*/
while (low < high) {
// 比较高位元素 & 枢纽元素
while (low < high && srcArray[high] >= tmp) {
high--;
}
int temp = srcArray[low];
srcArray[low] = srcArray[high];
srcArray[high] = temp;
// 比较低位元素 & 枢纽元素
while (low < high && srcArray[low] <= tmp) {
low++;
}
int temp1 = srcArray[high];
srcArray[high] = srcArray[low];
srcArray[low] = temp1;
}
// 最终低位、高位都会指向枢纽位置,返回
return low;
}
/**
* 执行 快速排序
*/
public static void main(String[] args) {
// 定义待排序数列
int[] src = new int[]{ 50, 10, 90, 30, 70, 40, 80, 60, 20 };
// 输出结果
quickSort(src,0,src.length-1);
// 输出 排序后的序列
for (int a = 0; a < src.length; a++) {
System.out.println(src[a]);
}
}
}
枢轴位置 =50
枢轴位置 =30
枢轴位置 =10
枢轴位置 =80
枢轴位置 =60
10
20
30
40
50
60
70
80
90
主要修改点时在分区函数
Partition()
中
/**
* 参数说明:
* @param srcArray = 需排序的数组序列
* @param low = 数组第1个元素下标
* @param high = 数组最后1个元素下标
*/
public static int Partition(int[] srcArray, int low, int high) {
// 1. 将子表的第1个个记录作为枢纽
int tmp = srcArray[low];
while (low < high) {
// 2. 比较高位元素 & 枢纽元素
while (low < high && srcArray[high] >= tmp) {
high--;
}
// 采用 替换操作 换掉之前的 交换操作
srcArray[low] = srcArray[high];
// 之前的交换操作
// int temp = srcArray[low];
// srcArray[low] = srcArray[high];
// srcArray[high] = temp;
// 3. 比较低位元素 & 枢纽元素
while (low < high && srcArray[low] <= tmp) {
low++;
}
// 采用 替换操作 换掉之前的 交换操作
srcArray[high] = srcArray[low];
// 之前的交换操作
// int temp1 = srcArray[high];
// srcArray[high] = srcArray[low];
// srcArray[low] = temp1;
}
// 将枢轴元素替换到当前低位指针指向的元素 & 返回
srcArray[low] = tmp;
return low;
}
public class QuickSort {
/**
* 快速排序算法实现(优化 = 减少不必要的交换)
* 参数说明:
* @param srcArray = 需排序的数组序列
* @param low = 数组第1个元素下标
* @param high = 数组最后1个元素下标
*/
public static void quickSort(int[] srcArray, int low, int high) {
if (low < high) {
// 1. 将待排序列 根据所选的枢纽位置,分割成独立的2个子序列
// 最终返回的是枢纽位置(主要优化在取枢纽值里)
int privot = Partition(srcArray, low, high);
// 2. 分别对这2个子序列 进行排序
// a. 通过递归 对低字表进行排序
quickSort(srcArray, low, privot - 1);
// b. 通过递归 对高字表进行排序
quickSort(srcArray, privot + 1, high);
}
}
/**
* 作用:将待排序列 根据所选的枢纽位置,分割成独立的2个子序列(优化 = 减少不必要的交换)
* 返回值:所选的枢纽位置
* 参数说明:
* @param srcArray = 需排序的数组序列
* @param low = 数组第1个元素下标
* @param high = 数组最后1个元素下标
*/
public static int Partition(int[] srcArray, int low, int high) {
// 1. 将子表的第1个个记录作为枢纽
int tmp = srcArray[low];
while (low < high) {
// 2. 比较高位元素 & 枢纽元素
while (low < high && srcArray[high] >= tmp) {
high--;
}
// 采用 替换操作 换掉之前的 交换操作
srcArray[low] = srcArray[high];
// 之前的交换操作
// int temp = srcArray[low];
// srcArray[low] = srcArray[high];
// srcArray[high] = temp;
// 3. 比较低位元素 & 枢纽元素
while (low < high && srcArray[low] <= tmp) {
low++;
}
// 采用 替换操作 换掉之前的 交换操作
srcArray[high] = srcArray[low];
// 之前的交换操作
// int temp1 = srcArray[high];
// srcArray[high] = srcArray[low];
// srcArray[low] = temp1;
}
// 将枢轴元素替换到当前低位指针指向的元素 & 返回
srcArray[low] = tmp;
return low;
}
/**
* 执行 快速排序
*/
public static void main(String[] args) {
// 定义待排序数列
int[] src = new int[]{ 50, 10, 90, 30, 70, 40, 80, 60, 20 };
// 输出结果
quickSort(src,0,src.length-1);
// 输出 排序后的序列
for (int a = 0; a < src.length; a++) {
System.out.println(src[a]);
}
}
}
10
20
30
40
50
60
70
80
90
资料显示,当序列的数据量>7时,视为大数据量序列
a. 直接插入排序是简单排序算法中性能最好的 b. 优化主要在
quickSort()
中
public class QuickSort {
/**
* 快速排序算法实现(优化 = 优化数据量较小序列的排序方案)
* 参数说明:
* @param srcArray = 需排序的数组序列
* @param low = 数组第1个元素下标
* @param high = 数组最后1个元素下标
*/
public static void quickSort(int[] srcArray, int low, int high) {
// 当序列的数据量>7时,视为大数据量序列,此时采用 快速排序
if (high-low > 7) {
if (low < high) {
System.out.println("采用快排");
int privot = Partition(srcArray, low, high);
quickSort(srcArray, low, privot - 1);
quickSort(srcArray, privot + 1, high);
}
}
else{
// 当序列的数据量<7时,视为小数据量序列,此时采用 直接插入排序
insertSort(srcArray);
System.out.println("采用直接插入排序");
};
}
/**
* 作用:将待排序列 根据所选的枢纽位置,分割成独立的2个子序列(优化 = 优化数据量较小序列的排序方案)
* 返回值:所选的枢纽位置
* 参数说明:
* @param srcArray = 需排序的数组序列
* @param low = 数组第1个元素下标
* @param high = 数组最后1个元素下标
*/
public static int Partition(int[] srcArray, int low, int high) {
// 1. 将子表的第1个个记录作为枢纽
int tmp = srcArray[low];
while (low < high) {
// 2. 比较高位元素 & 枢纽元素
// 若高位元素 > 枢纽元素,则继续比较前1个高位元素
// 若高位元素 < 枢纽元素,则交换当前高位元素 与 低位元素 位置、开始比较低位元素 与 枢纽元素
while (low < high && srcArray[high] >= tmp) {
high--;
}
int temp = srcArray[low];
srcArray[low] = srcArray[high];
srcArray[high] = temp;
// 3. 比较低位元素 & 枢纽元素
// 若低位元素 < 基准元素,则继续比较下1个低位元素
// 若低位元素 > 枢纽元素,就交换当前低位元素 与 高位元素 位置;重新开始比较高位元素 与 枢纽元素
while (low < high && srcArray[low] <= tmp) {
low++;
}
int temp1 = srcArray[high];
srcArray[high] = srcArray[low];
srcArray[low] = temp1;
}
// 最终低位、高位都会指向枢纽位置,返回
return low;
}
/**
* 直接插入排序 算法实现
*/
public static void insertSort(int[] srcArray) {
int i; // 用于存放当前插入数据记录的数组下标
int j; // 用于存放需要比较记录的下标
int temp; // 用于交换数据
// 从第1个数据记录 开始,该元素可以认为已经被排序
for(i = 0 ; i < srcArray.length ; i++)
{
temp = srcArray[i];
// 取出下一个数据记录,在已经排序的序列中从后向前扫描
// 将 当前数据记录 与 前面排序好的值进行比较
for(j = i ; j > 0 && temp < srcArray[j-1] ; j --)
{
// 按照顺序小 -> 大 将 当前需要插入的数据记录插入到合适位置 = 后移已排序好的元素 + 插入新的数据记录
// a. 后移已排序好的元素
srcArray[j] = srcArray[j-1];
}
// 插入新的数据记录
srcArray[j] = temp;
}
}
/**
* 执行 快速排序
*/
public static void main(String[] args) {
// 定义待排序数列
int[] src = new int[]{ 50, 10, 90, 30, 70, 40, 80, 60, 20 };
// 输出结果
quickSort(src,0,src.length-1);
// 输出 排序后的序列
for (int a = 0; a < src.length; a++) {
System.out.println(src[a]);
}
}
}
注:关于直接插入排序算法实现可自行了解。
采用快排
采用直接插入排序
采用直接插入排序
10
20
30
40
50
60
70
80
90
特别注意:此处的排序方式选择不只是第一次排序,而是贯穿 整个快速排序过程,即 由于快速排序过程 = 逐步划分成半子表的过程,所以最后几次的排序一定会使用 直接插入排序
quickSort()
中public class QuickSort {
/**
* 快速排序算法实现(优化 = 优化递归 = 采用 迭代操作 代替 递归操作)
* 参数说明:
* @param srcArray = 需排序的数组序列
* @param low = 数组第1个元素下标
* @param high = 数组最后1个元素下标
*/
public static void quickSort(int[] srcArray, int low, int high) {
// 将if 改成 While,原来操作为:if (low < high)
while (low < high) {
int privot = Partition(srcArray, low, high);
quickSort(srcArray, low, privot - 1);
low = privot +1 ;
// 将 尾递归中对高字表的排序 改成 low = privot +1,原来操作为:quickSort(srcArray, privot + 1, high);
// 原因:在第1次循环后,后1次循环中的Partition(srcArray, low, high) = quickSort(srcArray, privot + 1, high)
// 故可删去该递归
}
}
/**
* 作用:将待排序列 根据所选的枢纽位置,分割成独立的2个子序列(优化 = 优化递归 = 采用 迭代操作 代替 递归操作)
* 返回值:所选的枢纽位置
* 参数说明:
* @param srcArray = 需排序的数组序列
* @param low = 数组第1个元素下标
* @param high = 数组最后1个元素下标
*/
public static int Partition(int[] srcArray, int low, int high) {
// 1. 将子表的第1个个记录作为枢纽
int tmp = srcArray[low];
while (low < high) {
// 2. 比较高位元素 & 枢纽元素
while (low < high && srcArray[high] >= tmp) {
high--;
}
int temp = srcArray[low];
srcArray[low] = srcArray[high];
srcArray[high] = temp;
// 3. 比较低位元素 & 枢纽元素
while (low < high && srcArray[low] <= tmp) {
low++;
}
int temp1 = srcArray[high];
srcArray[high] = srcArray[low];
srcArray[low] = temp1;
}
// 最终低位、高位都会指向枢纽位置,返回
return low;
}
/**
* 执行 快速排序
*/
public static void main(String[] args) {
// 定义待排序数列
int[] src = new int[]{ 50, 10, 90, 30, 70, 40, 80, 60, 20 };
// 输出结果
quickSort(src,0,src.length-1);
// 输出 排序后的序列
for (int a = 0; a < src.length; a++) {
System.out.println(src[a]);
}
}
}
10
20
30
40
50
60
70
80
90
最终,总结4种优化方式 & 贴出最终优化后的快速排序算法
public class QuickSort {
/**
* 快速排序算法实现(优化 = 选取枢轴、减少不必要的交换次数、优化数据量较小序列的排序方案、将尾递归操作->迭代操作)
* 参数说明:
* @param srcArray = 需排序的数组序列
* @param low = 数组第1个元素下标
* @param high = 数组最后1个元素下标
*/
public static void quickSort(int[] srcArray, int low, int high) {
/**
* 优化3:优化数据量较小序列的排序方案 = 步骤8、9
*/
if (high-low > 7) {
// 8. 当序列的数据量>7时,视为大数据量序列,此时采用 快速排序
System.out.println("采用快排");
/**
* 优化4:将尾递归操作->迭代操作 = 步骤10、11
*/
// 10. 将if 改成 While,原来操作为:if (low < high)
while (low < high) {
int privot = Partition(srcArray, low, high);
quickSort(srcArray, low, privot - 1);
// 11. // 将 尾递归中对高字表的排序 改成 low = privot +1,原来操作为:quickSort(srcArray, privot + 1, high);
// quickSort_RecursionOp(srcArray, middle + 1, high);
low = privot +1 ;
}
}
else{
// 9. 当序列的数据量<7时,视为小数据量序列,此时采用 直接插入排序
insertSort(srcArray);
System.out.println("采用直接插入排序");
}
}
/**
* 快速排序算法中寻找中间元素 实现(优化 = 选取枢轴、减少不必要的交换次数、优化数据量较小序列的排序方案、将尾递归操作->迭代操作)
* 参数说明:
* @param srcArray = 需排序的数组序列
* @param low = 数组第1个元素下标
* @param high = 数组最后1个元素下标
*/
public static int Partition(int[] srcArray, int low, int high) {
/**
* 优化1:三数取中选取枢轴 = 步骤1、2、3、4、5
*/
// 1. 找出中间元素
int m = low + (high - low) /2;
// 2. 比较左、右端数据元素,保证左端较小
// 若左>右,就交换位置
if(srcArray[low]>srcArray[high]) {
int temp = srcArray[low];
srcArray[low] = srcArray[high];
srcArray[high] = temp;
}
// 3. 比较中、右端数据元素,保证中端较小
// 若中>右,就交换位置
if(srcArray[m]>srcArray[high]) {
int temp1 = srcArray[m];
srcArray[m] = srcArray[high];
srcArray[high] = temp1;
}
// 4. 比较中、左端数据元素,保证中端较小
if(srcArray[m]>srcArray[low]) {
// 若中>左,就交换位置
int temp2 = srcArray[m];
srcArray[m] = srcArray[low];
srcArray[low] = temp2;
}
// 此时,最低位 = srcArray[low] = 三数的中间数(即 最低位、最高位 & 中间数的中间值)
// 将上述值作为枢纽
int tmp = srcArray[low];
System.out.println("枢轴位置 =" + srcArray[low]);
/**
* 优化2:减少不必要的交换次数 = 步骤5.6.7
*/
while (low < high) {
while (low < high && srcArray[high] >= tmp) {
high--;
}
// 5. 采用 替换操作 换掉之前的 交换操作
srcArray[low] = srcArray[high];
// 之前的交换操作
// int temp = srcArray[low];
// srcArray[low] = srcArray[high];
// srcArray[high] = temp;
while (low < high && srcArray[low] <= tmp) {
low++;
}
// 6. 采用 替换操作 换掉之前的 交换操作
srcArray[high] = srcArray[low];
// 之前的交换操作
// int temp1 = srcArray[high];
// srcArray[high] = srcArray[low];
// srcArray[low] = temp1;
}
// 7. 将枢轴元素替换到当前低位指针指向的元素 & 返回
srcArray[low] = tmp;
return low;
}
/**
* 直接插入排序 算法实现
*/
public static void insertSort(int[] srcArray) {
int i; // 用于存放当前插入数据记录的数组下标
int j; // 用于存放需要比较记录的下标
int temp; // 用于交换数据
// 从第1个数据记录 开始,该元素可以认为已经被排序
for(i = 0 ; i < srcArray.length ; i++)
{
temp = srcArray[i];
// 取出下一个数据记录,在已经排序的序列中从后向前扫描
// 将 当前数据记录 与 前面排序好的值进行比较
for(j = i ; j > 0 && temp < srcArray[j-1] ; j --)
{
// 按照顺序小 -> 大 将 当前需要插入的数据记录插入到合适位置 = 后移已排序好的元素 + 插入新的数据记录
// a. 后移已排序好的元素
srcArray[j] = srcArray[j-1];
}
// 插入新的数据记录
srcArray[j] = temp;
}
}
/**
* 执行 快速排序
*/
public static void main(String[] args) {
// 定义待排序数列
int[] src = new int[]{ 50, 10, 90, 30, 70, 40, 80, 60, 20 };
// 输出结果
quickSort(src,0,src.length-1);
// 输出 排序后的序列
for (int a = 0; a < src.length; a++) {
System.out.println(src[a]);
}
}
}
枢轴位置 =50
采用直接插入排序
枢轴位置 =70
采用直接插入排序
枢轴位置 =80
采用直接插入排序
10
20
30
40
50
60
70
80
90
以下将分析算法的性能:时间复杂度、空间复杂度、稳定性