快速排序算法是冒泡排序算法的一种优化,我们知道冒泡排序算法是两层for循环嵌套,时间复杂度为O(N^2),并且每次移动的距离短,每次比较的元素都是相邻的元素。所以快速排序就弥补了这个缺点。
快排选用不同的规则的基准都是在选择固定基准(第一个元素,最后一个元素,中间元素)的基础上得来的,最核心的比较:以其中一个元素为基准比较为例:循环遍历左侧是否有比自己大的元素,右侧是否有比自己小的元素,将两个元素互相交换,然后再递归处理左右两个数组元素即可,具体代码如下:
AcWing 785. 快速排序
下面以一道例题为例:
给定你一个长度为 nn 的整数数列。
请你使用快速排序对这个数列按照从小到大进行排序。
并将排好序的数列按顺序输出。
输入格式
输入共两行,第一行包含整数 nn。
第二行包含 nn 个整数(所有整数均在 1∼1091∼109 范围内),表示整个数列。
输出格式
输出共一行,包含 nn 个整数,表示排好序的数列。
数据范围
1≤n≤1000001≤n≤100000
输入样例:
5
3 1 2 4 5
输出样例:
1 2 3 4 5
一.以第一个元素为基准
比较冗杂一般的代码
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
const int N = 1e6+10;
int vec[N];
//分隔
int partition(int vec[], int i, int j)
{
while (i < j)
{
while (i < j && vec[i] <= vec[j])
j--;
if (i < j)
{
swap(vec[i], vec[j]);
i++;
}
while (i < j && vec[i] <= vec[j])
i++;
if (i < j)
{
swap(vec[i], vec[j]);
j--;
}
}
return i;
}
void quickSort(int vec[], int i, int j)
{
if (i < j)
{
int pos = partition(vec, i, j);
quickSort(vec, i, pos - 1);
quickSort(vec, pos + 1, j);
}
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0;i < n;i++)
{
int num = 0;
scanf("%d", &num);
vec[i] = num;
}
quickSort(vec, 0, n-1);
for (int i = 0;i < n;i++)
{
printf("%d ", vec[i]);
}
return 0;
}
二.以中位数为基准
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
const int N = 1e6+10;
int vec[N];
//分隔
//取中位数作为分隔进行快速排序
int findMedianIndex(int vec[],int first,int middle,int end)
{
return (vec[first] > vec[middle])
? ((vec[middle] > vec[end]) ? middle : ((vec[first] > vec[end]) ? end : first))
: ((vec[first] > vec[end]) ? first : ((vec[middle] > vec[end]) ? end : middle));
}
int partition(int vec[], int i, int j)
{
int pivotIndex = findMedianIndex(vec, i, (i + j) / 2, j);
swap(vec[i], vec[pivotIndex]);
while (i < j)
{
while (i < j && vec[i] <= vec[j])
j--;
if (i < j)
{
swap(vec[i], vec[j]);
i++;
}
while (i < j && vec[i] <= vec[j])
i++;
if (i < j)
{
swap(vec[i], vec[j]);
j--;
}
}
return i;
}
void quickSort(int vec[], int i, int j)
{
if (i < j)
{
int pos = partition(vec, i, j);
quickSort(vec, i, pos - 1);
quickSort(vec, pos + 1, j);
}
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0;i < n;i++)
{
int num = 0;
scanf("%d", &num);
vec[i] = num;
}
quickSort(vec, 0, n-1);
for (int i = 0;i < n;i++)
{
printf("%d ", vec[i]);
}
return 0;
}
三. 以随机数为基准
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e6+10;
int vec[N];
void quick_sort(int vec[],int i,int j)
{
if(i >= j) return;
//取随机数看能否能过
int num = (int)(1.0*rand()/RAND_MAX*(j-i)+i);
swap(vec[i],vec[num]);
int x = vec[i],l = i-1,r = j+1;
while(i < j)
{
do l++; while(vec[l] < x);
do r--; while(vec[r] > x);
if(l < r) swap(vec[l],vec[r]);
}
quick_sort(vec,i,l-1);
quick_sort(vec,l+1,j);
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0;i < n;i++)
{
scanf("%d",&vec[i]);
}
/*quickSort(vec, 0, n-1);*/
sort(vec,vec+n);
for (int i = 0;i < n;i++)
{
printf("%d ", vec[i]);
}
return 0;
}
分析其时间复杂度:
在最好的情况O(nlog2n):最好的情况就是每次划分的左右两个数组长度完全相同。怎么说呢
每一组数组的快排时间复杂度为O(n)。递归的深度就是完全二叉树的深度nlog2n所以相乘可得。
最坏的情况就是O(n^2):就是当差不多已经排好序的情况下,分成的两组元素一组为空,递归的深度此时就是数组的大小。
空间复杂度就是递归的深度,他会占用系统额外的内存。
最好情况的系统开销就是完全二叉树的深度log2n
最坏情况的开销就是o(n)