时间复杂度:平均时间复杂度O(nlogn),最坏时间复杂度O(n*n)
空间复杂度:O(nlogn)
参考链接https://blog.csdn.net/qq_28081081/article/details/80597455
方法一:挖坑法
原文链接:https://www.cnblogs.com/qq931399960/p/9550026.html
(下图中单词有两处拼写错误,pviot和pvoit应该为pivot)
public class QuickSort {
public static void main(String[] args) {
int[] arr = {12, 45, 23, 67, 7, 1, 5, 21};
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] numbers, int low, int high) {
if (low >= high) {
return;
}
int middle = getMiddle(numbers, low, high); // 将numbers数组进行一分为二
quickSort(numbers, low, middle - 1); // 对低字段表进行递归排序
quickSort(numbers, middle + 1, high); // 对高字段表进行递归排序
}
/**
* 查找出中轴(默认是最低位low)的在numbers数组排序后所在位置
*
* @param numbers 带查找数组
* @param low 开始位置
* @param high 结束位置
* @return 中轴所在位置
*/
public static int getMiddle(int[] numbers, int low, int high) {
int temp = numbers[low]; // 数组的第一个作为中轴
while (low < high) {
while (low < high && numbers[high] >= temp) {
high--;
}
numbers[low] = numbers[high];// 比中轴小的记录移到低端
while (low < high && numbers[low] <= temp) {
low++;
}
numbers[high] = numbers[low]; // 比中轴大的记录移到高端
}
numbers[low] = temp; // 中轴记录到尾
return low; // 返回中轴的位置
}
}
方法二:左右指针法:
原文链接:https://www.cnblogs.com/qq931399960/p/9550026.html
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int[] arr = { 12, 45, 23, 67, 7, 1, 5, 21 };
quickSortChange(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
public static void quickSortChange(int[] arr, int startIndex, int endIndex) {
if (startIndex >= endIndex) {
return;
}
int partitionIndex = getPartitionIndex(arr, startIndex, endIndex);
printNumber("quickSortChange", arr);//打印
quickSortChange(arr, startIndex, partitionIndex - 1);
quickSortChange(arr, partitionIndex + 1, endIndex);
}
public static int getPartitionIndex(int[] arr, int startIndex, int endIndex) {
int left = startIndex;
int right = endIndex;
int pivot = arr[startIndex];
while (left != right) {
// 左侧索引必须小于右侧索引,当右侧数据大于基准元素,则将右侧元素向左移动一位,继续判断,直到找到比基准元素小的数据
while (left < right && arr[right] > pivot) {
right--;
}
// 左侧索引必须小于右侧索引,当左侧数据小于等于基准元素,则将左侧元素右移一位,继续判断,直到找到比基准元素大的数据位置
while (left < right && arr[left] <= pivot) { // 此处必须是左侧元素“小于等于”基准元素
left++;
}
if (left < right) {
// 通过以上两轮while循环,已经找到左侧大于基准元素的数据和右侧小于基准元素的数据,交换它们
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
}
// 此时right和left值是相同的,将基准元素与重合位置元素交换
arr[startIndex] = arr[left];
arr[left] = pivot;
return left;
}
}
第二种方法的另一种形式:
原文链接:https://winner.blog.csdn.net/article/details/47379295
数组int[] array = {12, 45, 23, 67, 7, 1, 5, 21};
首先将第一个元素选为基准点,从右端第一个元素开始扫描,找到第一个比12小的元素(5)时停止,两者交换位置{5, 45, 23, 67, 7, 1, 12, 21},然后从左端开始扫描,找到第一个比12大的元素(45)时停止,两者交换位置
{5, 12, 23, 67, 7, 1, 45, 21},周而复始,直到12找不到可交换的元素为止,至此一轮快速排序结束。
public class QuickSort {
int[] array = {12, 45, 23, 67, 7, 1, 5, 21};
public static void main(String[] args) {
recursiveQuikSort(0, array.length - 1);
}
/**
* 递归的快速排序
*
* @param low 数组的最小下标
* @param high 数组的最大下标
*/
private void recursiveQuikSort(int low, int high) {
if (low >= high) {
return;
} else {
int pivot = array[low]; //以第一个元素为基准
int partition = partition(low, high, pivot); //对数组进行划分,比pivot小的元素在低位段,比pivot大的元素在高位段
printNumber("recursiveQuikSort", array); //打印
recursiveQuikSort(low, partition - 1); //对划分后的低位段进行快速排序
recursiveQuikSort(partition + 1, high); //对划分后的高位段进行快速排序
}
}
private int partition(int low, int high, int pivot) {
while (low < high) {
while (low < high && array[high] >= pivot) { //从右端开始扫描,定位到第一个比pivot小的元素
high--;
}
swap(low, high);
while (low < high && array[low] <= pivot) { //从左端开始扫描,定位到第一个比pivot大的元素
low++;
}
swap(low, high);
}
return low;
}
/**
* 交换数组中两个元素的数据
*
* @param low 欲交换元素的低位下标
* @param high 欲交换元素的高位下标
*/
private void swap(int low, int high) {
int temp = array[high];
array[high] = array[low];
array[low] = temp;
}
}
算法分析
在归并排序中,我们详细推算了时间复杂度,快速排序与归并排序一样采取了分治算法,它的时间复杂度也是O(N*log2N)。
对于分治算法一般都是如此,用递归的方法把数据项分为两组,然后调用自身来分别处理每一组数据。算法实际上是以2为底,运行时间与N*log2N成正比。
对于快速排序来说,最理想的状态是随机分布的数据,即我们任意选定的枢纽处于中间位置,有一半元素小于它,有一半元素大于它。当数据时由小到大排列或者由大到小排列时,快速排序的效率最低,时间复杂度扩大为O(N2)。
选定第一个元素为枢纽实现起来确实很简单,但是当它为最大值或最小值时,快速排序的效率会严重降低。假如选中的元素为数组的中值,自然是最好的选择,但是却要遍历整个数组来确定中值,这个过程可能比排序花费的时间还长,得不偿失。折衷的方法是找到数组中的第一个、最后一个以及处于中间位置的元素,选出三者的中值作为枢纽,既避免了枢纽是最值的情况,也不会像在全部元素中寻找中值那样费时间。这种方法被称为“三项取中法”(median-of-three)。