一、快速排序介绍
1、优点
快速排序是一种原地排序算法,它只需要一个很小的栈空间来保存递归调用信息,而不需要额外的存储空间来存储待排序的元素。
2、时间复杂度
快速排序的时间复杂度在最理想的情况下是 O(n log n),其中 n 是待排序数组的元素数量。这发生在每次分区操作都能将数组划分为几乎相等的两部分时。
在最坏的情况下,当待排序的数组已经是有序的,或者输入数组的元素几乎相同,那么快速排序的时间复杂度可能会退化到 O(n^2)。这是因为分区操作可能每次只移动一个元素,导致算法需要进行多次递归调用,从而产生大量的比较和交换操作。
3、提高鲁棒性的手段
为了避免最坏情况的发生,可以采取一些优化措施,如随机化选择基准元素(pivot),或者使用三数取中等方法来选择基准元素,以提高算法的鲁棒性。
二、快速排序基本思想
快速排序的原理主要基于“分而治之”的思想。具体步骤如下:
- 首先,从待排序的数组中选取一个基准元素(pivot),通常选择第一个元素或者最后一个元素,或者通过某种方式随机选取一个元素。
- 接下来,通过一趟排序将待排序列分成两部分,其中一部分所有元素都比基准元素小,另一部分所有元素都比基准元素大(或者等于基准元素),此时基准元素在其排好序后的正确位置。
- 然后,递归地对这两部分数据进行快速排序。也就是说,对左边的子序列和右边的子序列分别重复执行步骤1和步骤2,直到整个序列有序。
三、快速排序java程序
package com.xmx.testDemo;
public class quikSort {
public static void main(String[] args){
int[] arr = {1,2,4,56,56,1,3,123,14,3,1,243,134,32,41};
quick1(arr,0,arr.length - 1);
for(int num : arr){
System.out.print(num + " ");
}
}
public static void quick1(int[] arr,int left,int right){
if(left < right){
//获取基准元素索引位置
int partitionIndex = getPartition(arr,left,right);
quick1(arr,left,partitionIndex - 1);
quick1(arr,partitionIndex + 1,right);
}
}
//手写快排
public static int getPartition(int[] arr,int left,int right){
int privot = arr[left];
int i = left;
int j = right;
//开始遍历
while(i < j){
while(i < j && arr[j] >= privot){
j--;
}
//此时左右指针没有相遇
if(i < j){
arr[i] = arr[j];
i++;
}
while(i < j && arr[i] < privot){
i++;
}
//此时左右指针没有相遇
if(i < j){
arr[j] = arr[i];
j--;
}
}
arr[j] = privot;
return j;
}
}
四、随机化选取和使用三数取中法
package com.xmx.testDemo;
import java.util.Random;
public class quikSort {
public static void main(String[] args){
int[] arr = {1,2,4,56,56,1,3,123,14,3,1,243,134,32,41};
System.out.print("排序前:");
for(int num : arr){
System.out.print(num + " ");
}
quick1(arr,0,arr.length - 1);
System.out.print("\n排序后:");
for(int num : arr){
System.out.print(num + " ");
}
}
public static void quick1(int[] arr,int left,int right){
if(left < right){
//获取基准元素索引位置
int partitionIndex = getPartition(arr,left,right);
quick1(arr,left,partitionIndex - 1);
quick1(arr,partitionIndex + 1,right);
}
}
//手写快排
public static int getPartition(int[] arr,int left,int right){
// 随机化选取基准元素索引
int randomPivotIndex = getRandomPivotIndex(left, right);
// 将基准元素交换到最左边
swap(arr, left, randomPivotIndex);
int privot = arr[left];
int i = left;
int j = right;
//开始遍历
while(i < j){
while(i < j && arr[j] >= privot){
j--;
}
//此时左右指针没有相遇
if(i < j){
arr[i] = arr[j];
i++;
}
while(i < j && arr[i] < privot){
i++;
}
//此时左右指针没有相遇
if(i < j){
arr[j] = arr[i];
j--;
}
}
arr[j] = privot;
return j;
}
// 随机化选取基准元素索引
private static int getRandomPivotIndex(int left, int right) {
Random rand = new Random();
//确保生成的数字在[left,right - left + 1)之间,不包含右边界值。
return left + rand.nextInt(right - left + 1);
}
// 交换数组中两个元素的位置
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
public class QuickSortMedianOfThree {
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pivotIndex = partition(arr, low, high);
quickSort(arr, low, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, high);
}
}
private static int partition(int[] arr, int low, int high) {
int mid = (low + high) / 2;
// 三数取中法选择基准元素
if (arr[mid] < arr[low]) {
swap(arr, mid, low);
}
if (arr[high] < arr[low]) {
swap(arr, high, low);
}
if (arr[mid] < arr[high]) {
swap(arr, mid, high);
}
// 此时arr[high]已经是三个数中的中位数,将它作为基准元素
swap(arr, high, high - 1); // 将基准元素放到倒数第二个位置
int pivot = arr[high - 1];
int i = low;
for (int j = low; j < high - 1; j++) {
if (arr[j] < pivot) {
swap(arr, i, j);
i++;
}
}
swap(arr, i, high - 1); // 将基准元素放到正确的位置
return i;
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void main(String[] args) {
int[] arr = {9, 7, 5, 11, 12, 2, 14, 3, 10, 6};
quickSort(arr, 0, arr.length - 1);
for (int num : arr) {
System.out.print(num + " ");
}
}
}