快速排序!名字就叫“ 快速 ”排序,你服不服?

快速排序!名字就叫“ 快速 ”排序,你服不服?

快速排序

一、快排算法有什么优点,为什么称之为“快”排?

QuickSort是对归并排序算法的优化,继承了归并排序的优点,同样应用了分治思想。

1.如何“分”?(如何缩小问题的规模)

2.如何“治”?(如何解决子问题)

快排的前身是归并,归并的最大问题是需要额外的存储空间,并且由于合并过程不确定,致使每个元素在序列中的最终位置上不可预知的。针对这一点,快速排序提出了新的思路:把更多的时间用在“分”上,而把较少的时间用在“治”上。从而解决了额外存储空间的问题,并提升了算法效率。

快排之所以被称为“快”排,是因为它在平均时间上说最快的,主要原因是每趟快排需要指定一个“基准值”(也就是作为分界点的值),一趟中涉及的所有比较都是与这个“基准值”来进行比较的,效率自然大大提高。除此之外,快排的高效率与分治思想也是分不开的。

基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

二、如何“分”?(即如何确定“基准值”)

确定一个“基准值”,将数组分为两个部分(比基准值小的 | 比基准值大的)

如何确定这个基准值呢?

1、随机

在所要排序的数组中,随机选取一个数来作为基准值,需要将其交换到最边上。

2、直接取最边上的值(任选左右)。

3、三数取中法

因为前两种取值方法,有大概率取到要排数组的最大值或者最小值,导致所分的两部分的其中一侧没有元素(选取最边上的元素作为基准值,并且数组已经有序或数组逆序,都是最坏情况),为了避免出现这种情况,我们采用三数取中法。

在[ left, left +(right -left)/2, right ] 中,通过判断,选取其中大小为 中 的元素。

例如:9 8 3 4 5 7 2 其下标 0~6 ,left = 0,mid = 3,right = 6; 比较array[left] ,array[mid] ,array[right],得到基准值为“4”。这样就有效避免了取到 极值 的可能。

代码如下:

private static int 三数取中(int[] array,int left, int right) {

int mid = left +(right -left)/2;

if (array[left] > array[right]){

if(array[left] < array[mid]){

return left;

}else if (array[mid] > array[right]){

return mid;

}else{

return right;

}

}else{

if (array[right] < array[mid]){

return right;

}else if(array[mid] > array[left]){

return mid;

}else{

return left;

}

}

}

如何将比基准值小的放左,大的放右?

1、Hover

代码如下:

//1、Hover

private static int parition_01(int[] array,int left,int right){

int begin = left;

int end = right;

int pivot = array[right];

while (begin < end){

while (begin

begin++;

}

while (begin < end && array[end] >= pivot){

end--;

}

Swap(array,begin,end);

}

Swap(array,begin,right);

return begin;

}

Swap:

private static void Swap(int array[],int a,int b){

int t = array[a];

array[a] = array[b];

array[b] = t;

}

2、挖坑法

代码如下:

//2、挖坑法

private static int parition_02(int[] array,int left,int right){

int begin = left;

int end = right;

int pivot = array[right];

while (begin < end){

while (begin

begin++;

}

array[end] = array[begin];

while (begin < end && array[end] >= pivot){

end--;

}

array[begin] = array[end];

}

array[begin] = pivot;

return begin;

}

3、前后下标

代码如下:

//3、前后下标

private static int parition_03(int[] array,int left,int right){

int back = left;

int pivot = array[right];

for (int i = left; i <= right; i++){

if(array[i] < pivot){

Swap(array,back,i);

back++;

}

}

Swap(array,back,right);

return back;

}

三、如何“治”?(怎么处理已经分好了的区间)

代码如下:

private static void quickSortInner(int[] array, int left, int right) {

// 直到 size == 1 || size == 0

if (left == right) {

// size == 1

return;

}

if (left > right) {

// size == 0

return;

}

// 要排序的区间是 array [left, right]

// 1. 找基准值,array[right];

int originIndex = sanShuQuZhong(array, left, right);

swap(array, originIndex, right);

// 2. 遍历整个区间,把区间分成三部分

int pivotIndex = partition3(array, left, right);

// 比基准值小的 [left, pivotIndex - 1]

// 比基准值的 [pivotIndex + 1, right]

// 3. 分治算法

// 处理比基准值小的区间

quickSortInner(array, left, pivotIndex - 1);

// 处理比基准值大的区间

quickSortInner(array, pivotIndex + 1, right);

}

public static void quickSort(int[] array) {

quickSortInner(array, 0, array.length - 1);

}

快速排序总结:

1.时空复杂度:快速排序每次将待排序数组分为两个部分,在理想状况下,每一次都将待排序数组划分成等长两个部分,则需要logn次划分。而在最坏情况下,即数组已经有序或大致有序的情况下,每次划分只能减少一个元素,快速排序将不幸退化为冒泡排序,所以快速排序时间复杂度下界为O(nlogn),最坏情况为O(n^2)。

实际应用中,快速排序的平均时间复杂度为O(nlogn)。快速排序在对序列的操作过程中只需花费常数级的空间。空间复杂度为 O(log(n))。但需要注意递归栈上需要花费最少logn最多n的空间。

2.稳定性:不稳定

相关数据

深度评测:豪8印象翡翠手镯品质、款式与购买指南
bt365验证不通过

深度评测:豪8印象翡翠手镯品质、款式与购买指南

📅 06-28 👁️ 7587
江南百景圖破衣服在哪 破衣服獲取方法介紹
365bet体坛即时比分

江南百景圖破衣服在哪 破衣服獲取方法介紹

📅 07-09 👁️ 4923
阴阳师小松丸最多刷新点介绍
bt365验证不通过

阴阳师小松丸最多刷新点介绍

📅 07-11 👁️ 158