1、快速排序基本思想
快速排序被认为是一种最好的内部排序方法。其基本思想是:任取待排序序列中的某一个元素作为基准,通过一趟快速排序将待排序的元素分割成左右两个子序列,其中左子序列元素的排序关键字均比基准(也称枢轴)元素的关键字值小;右子序列元素的关键字均比基准元素的关键字大,基准元素得到了它在整个排序中的最终位置并存放好,这个过程称为一趟快速排序。第二趟再分别对分割成左右两部分的子序列,进行快速排序,这个两部分子序列中的枢轴元素也得到了最终在序列中的位置并存放好,并且它们又分别分割出左右独立的两个子序列…..。显然,这是一个递归的过程,不断进行下去,直到每个待排序的子序列中只有一个记录时为止,整个排序过程结束。快速排序是对冒泡排序的一种改进。
这里有问题,就是如何把一个序列分成左右两部分?通常是以序列中第一个元素的关键字作为基准(基准)元素记录。
2、具体做法
设特排序列的下界和上界分别为low和high,R[low]是枢轴元素,一趟快速排序的具体过程如下:
(1)首先将R[low]中的记录保存盗pivot变量中,用两个整型变量i,j分别指向low和high所在的位置上的记录;
(2)先从j所指的记录起自右向左逐一将关键字和pivot.key进行比较,当找到第1个关键字小于pivot.key的记录时,将此记录复制到i所指的位置上去;
(3)然后从i+1所指的记录起自左向右逐一将关键字和pivot.key进行比较,当找到第1个关键字大于pivot.key的记录时,将该记录复制到j所指的位置上去;
(4)接着再从j-1所指的记录重复以上的(2)、(3)两步,直到i=j为止,此时将pivot中的记录放回到i或j的位置上,一趟快速排序完成。
3、快速排序的代码如下
package com.sort;
public class QuickSort {
public void quickSort(int[] array) {
QSort(array, 0, array.length - 1);
}
private void QSort(int[] array, int low, int high) {
int pivot;
if (low < high) {
pivot = partition(array, low, high);// 将array[low....high]一分为二
// ,算出枢轴值pivot
QSort(array, low, pivot - 1);// 对低子表递归排序
QSort(array, pivot + 1, high);// 对高子表递归排序
}
}
// 交换顺序表中array的子表的记录,使枢轴记录到位,并返回其所在的位置
private int partition(int[] array, int low, int high) {
int pivotkey;
pivotkey = array[low];// 用子表的第一个记录作枢轴记录
int temp = 0;
while (low < high) {// 从表的两端交替向中间扫描
while (low < high && array[high] >= pivotkey) {
high--;
}
// 将比枢轴记录小的记录交换到低端
temp = array[low];
array[low] = array[high];
array[high] = temp;
while (low < high && array[low] <= pivotkey) {
low++;
}
// 将比枢轴记录大的记录交换到高端
temp = array[low];
array[low] = array[high];
array[high] = temp;
}
return low;// 返回枢轴所在位置
}
}
4、测试
public class TestMain {
public static void main(String[] args) {
// int array[] = { 5, 1, -1, 0, 3 };
int array[] = { 50, 10, 90, 30, 70, 40, 80, 60, 20 };
QuickSort q = new QuickSort();
q.quickSort(array);
// 输出最后结果
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
}
输出结果:
10 20 30 40 50 60 70 80 90
5、算法分析
这段代码的核心是”pivot = partition(array, low, high);”,再执行它之前,array的数组值为{ 50, 10, 90, 30, 70, 40, 80, 60, 20 }。partition函数要做的就是先选取当中的一个关键字,比如选择第一个关键字50,然后想尽一切办法将它放到一个位置,使得它左边的值都比它小,右边的值都比它大,我们将这样的关键字称之为枢轴(pivot )。
在经过partition(array, 0, 8)的执行之后,数组变成{20,10,40,30,50,70,80,60,90},并返回值5给pivot,数字5表明50放置在数组下标为5的位置。此时,计算机把原来的数据变成了两个位于50左和右小数组{20,10,40,30}和{70,80,60,90},然后的递归调用”QSort(array, 0, 5 - 1);”和”QSort(array, 5 + 1, 8);”语句,其实就是对{20,10,40,30}和{70,80,60,90}分别进行同样的partition操作,直到顺序全部正确为止。
(1)、当程序开始执行,此时low=0,high=8。我们将array[low]=array[0]=50(用子表的第一个记录作枢轴记录)赋值给枢轴变量pivotkey。(low=0 )<( high=8)条件满足进入最外层内部循环语句。
(2)、array[high]=array[8]=20 >= pivotkey=50条件不成立,不进入内部第一个while循环内,继续执行内部第一个while循环之后的代码,因为array[high]=array[8]=20是一个比pivotkey=50还要小的值,因此它应该交换到50的左侧,交换array[low]=array[0]=50与array[high]=array[8]=20的值,使得array[low]=array[0]=20,array[high]=array[8]=50。此时输出为:20 10 90 30 70 40 80 60 50
(3)、继续执行内部第二个循环,( array[low]=array[0]=20 )< pivotkey=50, 所以执行low++,此时low=1。继续循环,( array[low]=array[1]=10 )< pivotkey=50,所以继续low++;此时low=2,array[low]=array[2]=90 )< pivotkey=50,条件不成立,退出内部第二个循环。继续执行后面的代码,交换array[low]=array[2]=90与array[high]=array[8]=50,使得array[low]=array[2]=50,array[high]=array[8]=90 即交换到50的右侧。此时输出为:20 10 50 30 70 40 80 60 90
(4)、由于(low=2)<(high=8),继续执行最外层的while循环。
(5)、当high=8时,array[high]=array[8]=90;所以array[high]>pivotkey=50,所以执行high–,此时high=7,arry[high]=array[7]=60,60>50,继续循环,执行high–,此时high=6,arry[high]=array[6]=80,80>50,继续循环,继续high–,此时high=5,arry[high]=array[5]=40,40>50条件不成立,退出内部第一个while循环。
(6)将比枢轴记录小的记录交换到低端,即arry[high]=array[5]=40交换到arry[low]=array[2]=50的左侧,此时的low=2,high=5,pivotkey=50;此时输出为:20 10 40 30 70 50 80 60 90
(7)、继续执行内部第二个循环,( array[low]=array[2]=40 )< pivotkey=50, 所以执行low++,此时low=3。继续循环,( array[low]=array[3]=30 )< pivotkey=50,所以继续low++;此时low=4,array[low]=array[4]=70 )< pivotkey=50,条件不成立,退出内部第二个循环。继续执行后面的代码,交换array[low]=array[4]=70与array[high]=array[5]=50,使得array[low]=array[4]=50,rray[high]=array[5]=70 即交换到50的右侧。此时low=4,high=5,pivotkey=50。此时输出为:20 10 40 30 50 70 80 60 90
(8)、由于(low=4)<(high=5),继续执行最外层的while循环。
(9)、当high=5时,array[high]=array[5]=70;所以array[high]>pivotkey=50,所以执行high–,此时high=4=low,arry[high]=array[4]=50,50>50,条件不成立,执行内部第一个while之后的代码,进行自己与自己交换,所以不变。
(10)、由于high=low,所以不进入内部第二个循环,继续执行后面的代码,进行自己与自己交换,所以不变。此时输出为:20 10 40 30 50 70 80 60 90
(11)、把low=4返回出去,此时整个数据分割成了两个字数组一个是比50都小的左子序列{20 10 40 30},一个是比50都大的右子序列{70 80 60 90}。其实就是将选取的pivotkey不断交换,将比它小的换到它的左边,比它大的换到它的右边,它也在交换中不断更改自己的位置,直到完全满足这个要求为止,后面就是分别对左右子序列表进行排序,原理和上面的一致,整个过程是一个递归的过程。
6、算法效率分析
7、常用排序方法比较