1、快速排序的思路:
- 在一组数据中选择一个数作为基准值,记为
pivot
;- 将数组中小于基准值的放在基准值的左边,大于基准值的放在基准值的右边;
- 对每个分割出来的区间递归处理。
示意图:
下面对以上三步各自做一定说明:
1.选择基准值pivot
:
1). 选择数组最左/右边的数;
2). 选择数组的最左边的数和最右边的数以及最中间的数,在三个数中任意选择一个;
3). 在数组中随机选择
2.数组分成小的区间(左边小右边大):
1). 左右指针法;
2). 挖坑法;
3). 前后指针法
2、区间划分:
快速排序的重难点就在于如何将数组分成小的区间,下面对这几种方法进行说明:
1). 左右指针法
思想 :
选取最右端的值为基准值;
- 遍历整个数组,当begin指向的值小于基准值时,begin++;
当begin指向的值大于基准值时,将begin指向的值和end指向的值进行交换;Meanwhile,当end指向的值大于基准值时,end–;
当end指向的值小于基准值时,将begin指向的值和end指向的值进行交换;- 遍历完一次后,将此时begin指向的值和基准值进行交换
下图示是第一次将数组排序成左小右大的过程,而快速排序整体上来看是一个递归的过程,因此每次进入Partition
函数时,基准值pivot
都会有所更新。
2).挖坑法
思想:
- 选取最右端的值为基准值;
- 遍历整个数组,当begin指向的值小于基准值时,begin++;
当begin指向的值大于基准值时,将begin指向的值赋给基准值,此时,begin所指向的位置为空;
Meanwhile,当end指向的值大于基准值时,end–;
当end指向的值小于基准值时,将end指向的值赋给begin指向的空值;- 两者交替进行;
- 遍历结束,将基准值赋给begin指向的位置
图示是一次遍历的过程:
3).前后指针法
思想:
- 选取最右端的值为基准值;
- 用cur遍历整个数组,当cur指向的值大于基准值时,cur++;
当cur指向的值小于基准值时,将cur指向的值和div指向的值进行交换;- 交换完毕后,div++
- 遍历结束,交换cur和div的值
图示是一次遍历的过程:
3、稳定性、时间复杂度与空间复杂度:
如图示:快速排序是一个简而治之的过程,即一分二,二分四……
划分完的小区间数呈指数性增长。设划分结束后,共有n个区间,则总共划分的次数为log(n),而在每一个小区间内都需要遍历一次,则它的时间复杂度为n * log(n)。
快速排序是一个递归的过程,参考上面的树,即可得出空间复杂度为log(n)。
稳定性:快速排序显然是不稳定的
时间复杂度:
最好:O(n * log(n))
平均:O(n * log(n))
最坏:O(n^2)
空间复杂度:
最好:O(log(n))
平均:O(log(n))
最坏:O(n)
最后附上代码:
void Swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int Partition_1(int array[], int left, int right)//左右下标
{
int begin = left;
int end = right;
int pivot = array[right];
//当pivot初值选择的是数组最右端的数时,要先走begin,
//否则如果序列已经有序,begin所在的数大小并没有参与比较,无法得出它的大小
while (begin < end)
{
while (begin < end&&array[begin] <= pivot)
{
begin++;
}
if(begin == end)
{
break;
}
while (begin < end&&array[end] >= pivot)
{
end--;
}
if(begin == end)
{
break;
}
Swap(array + begin, array + end);
}
Swap(array + begin, array + right);
return begin;
}
int Partition_2(int array[], int left, int right)//挖坑
{
int begin = left;
int end = right;
int pivot = array[right];
while (begin < end)
{
while (begin < end&&array[begin] <= pivot)
{
begin++;
}
array[end] = array[begin];
while (begin < end&&array[end] >= pivot)
{
end--;
}
array[begin] = array[end];
}
array[begin] = pivot;
return begin;
}
int Partition_3(int array[], int left, int right)//前后指针
{
int div = left;
int pivot = array[right];
for (int cur = left; cur <= right; cur++)
{
if (array[cur] < pivot)
{
Swap(array + cur, array + div);
div++;
}
}
Swap(array + div, array + right);
return div;
}
void _QuickSort(int array[], int left, int right)
{
if (left == right)
{
return;
}
if (left > right)
{
return;
}
int div = Partition_3(array, left, right);
_QuickSort(array, left, div - 1);
_QuickSort(array, div + 1, right);
}
void QuickSort(int array[], int size)
{
_QuickSort(array, 0, size - 1);
}