快速排序
在解决普通的排序问题时,我们通常使用的是较为简单的排序算法,但是这些简单的排序算法是存在一些问题的,比如我们常用的选择排序。
int a[N];
for(int i=0;i<n;i++)
{
for(int j=i;j<n;j++)
if(a[i]>a[j])swap(a[i],a[j]);
}
我们可以看出,这里书写了一个嵌套的循环,那么就可知在执行这段代码时的时间复杂度为O(n^2)。
那么倘若我们需要进行计算的数据量非常大的时候呢?那样我们所需要的计算时间就比较长了。而为了解决这个问题,我们可以学习其他的排序算法。
首先我们先学习快速排序。下图为快速排序的模板。
const int N=100010;
int a[N];
void sort(int left,int right)
{
if(left>=right)return;
int le=left-1,ri=right+1,mid=a[left+right>>1];
while(le<ri)
{
do le++;while(a[le]<mid);
do ri--;while(a[ri]>mid);
if(le<ri)swap(a[le],a[ri]);
}
sort(left,ri),sort(ri+1,right);
return;
}
由以上代码我们可以计算快速排序的时间复杂度。
首先可知
T
(
n
)
=
O
(
1
)
(
n
=
1
)
T
(
n
)
=
2
T
(
n
/
2
)
+
c
n
(
n
>
1
)
T(n)=O(1) (n=1)\\ T(n) =2T(n/2)+cn(n>1)\\
T(n)=O(1)(n=1)T(n)=2T(n/2)+cn(n>1)
由此可推
T
(
n
)
=
2
T
(
n
/
2
)
+
c
n
=
2
2
T
(
n
/
2
2
)
+
2
c
n
=
2
3
T
(
n
/
2
3
)
+
3
c
n
.
.
.
.
.
.
.
.
.
=
2
k
T
(
n
/
2
k
)
+
k
c
n
=
n
T
(
1
)
+
k
c
n
=
n
O
(
1
)
+
k
c
n
=
n
+
c
n
l
o
g
2
n
=
n
(
1
+
c
l
o
g
2
n
)
=
O
(
n
l
o
g
2
n
)
T(n) =2T(n/2)+cn\\ =2^{2}T(n/2^{2})+2cn\\ =2^{3}T(n/2^{3})+3cn\\ .........\\ =2^{k}T(n/2^{k})+kcn\\ =nT(1)+kcn\\ =nO(1)+kcn\\ =n+cnlog_2n\\ =n(1+clog_2n)\\ =O(nlog_2n)
T(n)=2T(n/2)+cn=22T(n/22)+2cn=23T(n/23)+3cn.........=2kT(n/2k)+kcn=nT(1)+kcn=nO(1)+kcn=n+cnlog2n=n(1+clog2n)=O(nlog2n)
通过上述过程我们计算出快速排序的时间复杂度为O(nlogn)。时间复杂度要小于选择与冒泡等基础的排序方法。
接下了我们举一个简单的例子来讲快速排序的逻辑过程。
例如一串数字
2 3 5 6 4 7
首先我们确定这串数组的左右边界。左端点位2,右端点位7。然后确定一个中间点(并不一定是中间值,只要是数组中的一个数字即可)我们选择5。
然后从左端开始2小于5向右移一位,
然后3小于5继续向右移动,直到所在位置的值大于5停止。(这时左指针在6的位置)。
然后判断右端指针,指向的数值为7大于5向左移动
然后4小于5停止。(这时右指针指向4)
现在左指针所在位置的下标小于右指针所指向的下标,交换这两个位置的数值。这时数字为
2 3 5 4 6 7
重复上述操作,知道左指针所在位置的下标大于等于右指针所指向的下标结束操作。
结束操作时数字为
2 3 5 4 6 7
然后以左端点以及使右指针为右端点为
2 3 5 4
重复之前的操作。
同时以右端点以及使右指针加一的位置为左端点为
6 7
重复之前的操作。最后即可得到正确的顺序。
上诉数字所有过程的序列为
2 3 5 6 4 7
2 3 5 4 6 7
(2 3 4 5) (6 7)
2 3 4 5 6 7
萌新书写,不清晰的地方多多见谅,欢迎交流。