之前我们介绍了交换排序中的冒泡排序,这次我们介绍了另一种交换排序,叫做快速排序。快速排序的优点是排序到位,不占用额外的空 room,时间复杂度为O(nlogn)。
当然,对于快速排序来说,它也有缺点。对于有大量重复元素的数组排序效率非常低,时间复杂度会降低到O (n 2)。这时候就需要使用改进的快速排序——双向快速排序。在双向快速排序的基础上,我们进一步优化了三向快速排序。
快速排序
快速排序的基本思想是:通过一次排序,把要排序的数据分成两个独立的部分,其中一部分的所有数据小于另一部分的所有数据,然后按照这种方法对这两部分数据进行快速排序。整个排序过程可以递归进行,使整个数据成为有序序列。
快速排序的步骤如下:
1.以第一个元素为分界点,用l指向它。

2.遍历右边的元素。在遍历的过程中,我们对数组进行排序,有些小于V,有些大于V,J指向小于V和大于V的边界点,I指向当前访问的元素e,此时数组arr
3.如果E >: V,那么在大于V的部分之后直接合并E,然后i++会继续比较后面的元素。

4.如果E < V,那么将E移动到J指向的元素的下一个元素,然后j++,然后i++继续比较后面的元素。

5.使用此方法遍历整个数组一次。遍历后,数组分为三部分,左边部分是V,中间部分是>:V,右边部分是

6.最后我们把L指向的元素和J指向的元素互换,这样元素V就排序很快了。V左边的元素都小于V,右边的元素都大于V。

现在我们用上面的方法来比较这个数组
快速分类代码:
公共静态空排序(可比较的
上面,我们使用左边的第一个元素作为校准元素。现在,我们随机选择一个元素作为校准元素。此时,第一次选择第一个元素的概率为1/n,第二次选择第二个元素的概率为1/n-1,以此类推。在它发生之前退化成链表的概率是1/n(n-1)(n-2)& # 8230;。,当n较大时,这个概率几乎为零。
另一个优化是对小规模数组使用插入排序,因为递归会让小规模问题中方法的调用过于频繁,而插入排序对于小规模数组来说速度非常快。
优化的快速分类代码:
公共静态空排序(可比较的
2.从I开始向后遍历,如果遍历的元素e < V,继续向后遍历,直到遍历的元素e >: =v,遍历停止。从j向前遍历,如果被遍历的元素e >: V,继续向前遍历,直到被遍历的元素e

3.交换I指向的元素和J指向的元素。然后i++,j & # 8211继续对比下一个。

双向快速分类代码:
公共静态空排序(可比较的
2.从I向后遍历,如果遍历的元素e=v,那么E直接合并到=v部分,然后i++继续遍历。如果遍历的元素e : V,那么e和>:Part v交换前面的元素(gt-1指向的元素),然后gt & # 8211但此时不需要更改I,因为I位置的元素与gt位置前面的空 white元素进行了交换。
3.遍历后,i=gt,然后把L指向元素和lt指向元素交换。

4.右< Part v sum >: Part v做上面的操作。
与双向快速排序相比,三向快速排序的优点是减少了重复元素的比较操作,因为重复元素在一次排序中已经被排列为单个部分,然后只需要对不等于重复元素的其他元素进行排序。
三向快速分类代码:
公共静态void排序(Comparable[] arr) {
int n = arr.length
sort(arr,0,n & # 82111);
}
私有静态void排序(Comparable[] arr,int l,int r) {
//对于小规模数组,使用插入排序
如果(r & # 8211l & lt= 15) {
InsertionSort.sort(arr,l,r);
返回;
}
//在arr[l & # 8230;R],选择一个数值作为校准点枢轴。
swap(arr,l,(int)(*th . random()*(r & # 8211;l+1))+l);
可比v = arr[l];
int lt = l;//arr[l+1 & # 8230;lt]& lt;v
int gt = r+1;//arr[gt & # 8230;r]& gt;v
int I = l+1;//arr[lt+1 & # 8230;i) == v
while(我& ltgt) {
if (arr[i]。compare to(v)& lt;0) {
swap(arr,I,lt+1);
i++;
lt++;
} else if (arr[i]。compare to(v)gt;0) {
swap(arr,I,gt & # 82111);
gt & # 8211;
} else { // arr[i] == v
i++;
}
}
swap(arr,l,lt);
sort(arr,l,lt & # 82111);
sort(arr,gt,r);
}
摘要
介绍了快速排序、快速排序优化、双向快速排序和三向快速排序。
对于快速排序,我们需要选择合适的校准点,使校准点两侧平衡;在快速排序中递归到一个小数组时,我们可以用插入排序代替递归来减少不必要的开销。
对于双向快速排序和三向快速排序,当数组中有大量重复元素时,我们使用它。
最后建议JDK的底层排序采用插入排序+双向快速排序+归并排序的组合。