快速排序(Quick Sort)
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
算法描述
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
从数列中挑出一个元素,称为 “基准”(pivot);
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
Java代码:
// Java数组类型属于对象类型,方法传参采用引用传递,
// 这里对 array 的修改其实是对原数组的修改,所以可以使用void返回类型
static void QuickSort(int[] array,int start,int end) {
if(array==null || array.length<2 || start<0 || end<=start) {
return ;
}
int pos = Qsort(array, start, end); //将array[start......end]数组分区
QuickSort(array,start,pos-1); //将分区后的左边部分,再次快速排序
QuickSort(array,pos+1,end); //将分区后的左边部分,再次快速排序
}
static int Qsort(int[] array,int start,int end) {
int pivot = array[start]; //选择要分区的数组的第一个元素为基准
while(start<end) {
while(start<end && array[end]>=pivot) end--; // 先从后往前找第一个比基准大的数,
array[start] = array[end]; // ①
while(start<end && array[start]<=pivot) start++; // 从前往后找第一个比基准小的数,
array[end] = array[start]; //②
}
array[start] = pivot;
return start;
}
在上述分区算法中,①②处对原始的分区算法做了改进。
原始算法:先从后往前找第一个比基准大的数,交换该数与基准;然后从前往后找第一个比基准小的数,交换该数与基准。
每次交换要执行3次赋值操作,而实际上中间过程对基准的赋值是无意义的,因为我们真正关心的是基准的位置,而不是基准的值(pivot 已经在最开始记录)。
所以我们可以直接在原数组中进行赋值操作,免去了分区过程中每次交互对基准的无意义赋值。
最后,start = end,我们将基准pivot赋值给array[start]即可,因为这才是最终基准所在的位置。
排序知识补充:
-
排序的定义
对一序列对象根据某个关键字进行排序。 -
术语说明
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
内排序:所有排序操作都在内存中完成;
外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
时间复杂度: 一个算法执行所耗费的时间。
空间复杂度:运行完一个程序所需内存的大小。
3 算法总结
图片名词解释:
n: 数据规模
k: “桶”的个数
In-place: 占用常数内存,不占用额外内存
Out-place: 占用额外内存