最好时间复杂度O(nlogn) 最坏时间复杂度O(N^2)
方法一:
左右指针法:
1 /**
2 * 双指针法,将基准点设置为最左端
3 *
4 * @param arr
5 * @param left
6 * @param right
7 */
8 public static void quicklySort(int[] arr, int left, int right) {
9
10 if (left >= right) {
11 return;
12 }
13
14 int base = arr[left];
15 int i = left;
16 int j = right;
17 while (i < j) {
18 while (j > i && arr[j] >= base) {
19 j--;
20 }
21 while (j > i && arr[i] <= base) {
22 i++;
23 }
24 if (i < j) {
25 int temp = arr[i];
26 arr[i] = arr[j];
27 arr[j] = temp;
28 }
29 }
30 arr[left] = arr[i];
31 arr[i] = base;
32 quicklySort(arr, left, i - 1);
33 quicklySort(arr, i + 1, right);
34
35 }
当基点设置在最左端时,要让右指针先移动(当基准设置在最右端时,要让左指针先移动)
S1 右指针移动找到第一个比基准小的数停止.
S2 左指针开始移动,找到第一个比基准大的数停止
S3 将左右指针所指的数进行交换
S4 继续上述步骤直到左右指针相遇,将其与基准进行交换
S5 按照基准的位置,分为左右两侧分别进行上述步骤,直到拆分到只剩一个元素 排序完毕
当基点设置在最左端时,要让右指针先移动(当基准设置在最右端时,要让左指针先移动)原因:
因为右指针最后停止的时候会和左指针发生交换,导致右指针停止的时候所指向的数要大于基准
若此时让左指针先进行移动,最后和两个指针相遇 (相遇在右指针的位置,此时右指针指向的数要大于基准)
若基准设置在最左端,会导致右指针指向的大于基准的数移动到左端,导致左端不是全部小于基准的数,排序失败
右指针先移动同理。
所以,当基准在最左端的时候,要右指针先移动,基准在最右端时,要左指针先移动。
方法二 挖坑法 (基准设在左边 右指针先走 和上面一样)
1 // 挖坑法
2 public static void quicklySort2(int[] arr, int left, int right) {
3
4 if(left >=right){
5 return ;
6 }
7
8 int base = arr[left];
9 int i = left;
10 int j = right;
11
12 while (i < j) {
13 // 右指针先走
14 while (j > i && arr[j] >= base) {
15 j--;
16 }
17 arr[i] = arr[j];
18
19 // 左指针再走
20 while (j > i && arr[i] <= base) {
21 i++;
22 }
23 arr[j] = arr[i];
24 }
25 arr[j] = base;
26 quicklySort2(arr, left, j - 1);
27 quicklySort2(arr, j + 1, right);
28
29 }
取基准的几种方法
详细见https://blog.csdn.net/hacker00011000/article/details/52176100
(1)固定位置
(2)随机选取基准, 选取后 将该位置的基准与最左端进行交换,即可以用常规的快排方法进行排序
(3)三数去中法。使用最左端,最右端和中间位置的三个元素,将这个三个元素进行排序,选取中间位置的数做为基准数,将其与最左端进行交换,即可用普通的快排进行处理。
四种优化方法
优化1:当待排序序列的长度分割到一定大小后,使用插入排序
优化2:在一次分割结束后,可以把与Key相等的元素聚在一起,继续下次分割时,不用再对与key相等元素分割
具体过程:在处理过程中,会有两个步骤
第一步,在划分过程中,把与key相等元素放入数组的两端
第二步,划分结束后,把与key相等的元素移到枢轴周围
void QSort(int arr[],int low,int high)
{
int first = low;
int last = high;
int left = low;
int right = high;
int leftLen = 0;
int rightLen = 0;
if (high - low + 1 < 10)
{
InsertSort(arr,low,high);
return;
}
//一次分割
int key = SelectPivotMedianOfThree(arr,low,high);//使用三数取中法选择枢轴
while(low < high)
{
while(high > low && arr[high] >= key)
{
if (arr[high] == key)//处理相等元素
{
swap(arr[right],arr[high]);
right--;
rightLen++;
}
high--;
}
arr[low] = arr[high];
while(high > low && arr[low] <= key)
{
if (arr[low] == key)
{
swap(arr[left],arr[low]);
left++;
leftLen++;
}
low++;
}
arr[high] = arr[low];
}
arr[low] = key;
//一次快排结束
//把与枢轴key相同的元素移到枢轴最终位置周围
int i = low - 1;
int j = first;
while(j < left && arr[i] != key)
{
swap(arr[i],arr[j]);
i--;
j++;
}
i = low + 1;
j = last;
while(j > right && arr[i] != key)
{
swap(arr[i],arr[j]);
i++;
j--;
}
QSort(arr,first,low - 1 - leftLen);
QSort(arr,low + 1 + rightLen,last);
优化3:优化递归操作
快排函数在函数尾部有两次递归操作,我们可以对其使用尾递归优化
优点:如果待排序的序列划分极端不平衡,递归的深度将趋近于n,而栈的大小是很有限的,每次递归调用都会耗费一定的栈空间,函数的参数越多,每次递归耗费的空间也越多。优化后,可以缩减堆栈深度,由原来的O(n)缩减为O(logn),将会提高性能。
优化4:使用并行或多线程处理子序列
来源:https://www.cnblogs.com/maxbolg/p/9355151.html