2.8 快速排序

文章介绍了快速排序算法的基本思想,包括分治策略的运用,如何通过基准数进行数组划分,并详细解析了算法过程,包括划分、递归求解和合并三个步骤。还讨论了最佳和最坏情况的时间复杂度,以及如何通过随机化选择基准元素来改进算法性能。
摘要由CSDN通过智能技术生成


问题引入:

方法:

我的理解是不断划分数组每次划分都找一个基准数把数组分为三部分大于基准数部分和小于基准书数部分还有等于基准数部分,在我看来这个方法更应该叫分治思想之基准数排序法

算法过程:

 划分过程

 算法动态演示过程:

 

快速排序算法是基于分治策略的另一个排序算法。其基本恩想是,对于输入的了象在 while 了
式是严格的,则alp:r],按以下三个步骤进行排序。应该交换a与
① 分解(Divide):以alp1为基准元素将 alp:划分成了段alp9-11,algl和algtl:9
while 循环
arp:-1]中任何一个元素小于等于 aigl,而alg+1中任何一个元素大于等于 algl。下众。
alp:g-1]中元素
划分过程中确定。
事实上
② 递归求解(Conquer):通过递归调用快速排序算法,分别对alp:9-1]和 alg+19
的元素放在房
出 ap门的下
排序。
③ 合并(Merge):由于对alp:g-11和 aig+1门的排序是就地进行的,因此在 apg-1
果选择 alry
g-r,这会作
alg+1:八1都己排好的序后,不需要执行任何计算,ap:门则己排好序。
对手输
基于这个思想,可实现快速排序算法如下:

template<class Type>
void QuickSort(Type a[],int p,int r) 
{
	if(p<r)
	{	
		int q=partition(a,p,r)
		QuickSort(a,p,q-1);//对左半段排序 
		QuickSort(a,q+1,r);//对右半段排序 
	}
}

对含有n个元素的数组a10:n-11进行快速排序只要调用 QuickSort(a,0,1-1)即可。
上述算法中的函数 Partition0以一个确定的基准元素 aip]对子数组aiD.门进行划分,它
快速排序算法的关键。

template<classs Type>
int Partition(Type a[],int p,int r)
{
    int i=p,j=r+1;
    Type x=a[p];//将小于x的元素交换到左边区域,将大于x的元素交换到右边区域
    while(true)
    {
        while(a[++i]<x&&i<r);
        while(a[--j]>x);
        if(i>=j)
        break;
        swap(a[i],a[j]);
    }
    a[p]=a[j];
    a[j]=x;
    return j;
}

Partition 对aip进行划分时,以元素x-21月1作为划分的基准,分别从左、右两端开始,
扩展两个区域alp:门和al:门,使alp:门中元素小于或等于x,而al:门中元素大于或等于x。初
始时,-p,且j=+1。
在while 循环体中,下标i还渐减小,i逐渐增大,直到 a订二x≥a61。如果这两个不等
式是严格的,则a门不会是左边区域的元素,而a11不会是右边区域的元素。此时若 门,就
应该交换 a1与 al的位置,扩展左右两个区域
while 循环重复至 记i时结束。这时 alp:门已被划分成 aip:g-1小alg]和alg+1:门,且满足
alp:9-1]中元素不大于 alg+1:门中元素。在Partition 结束时,返回划分点g-j。
0
事实上,西数 Partition0的主要功能是将小于×的元素放在原数组的左半部分,将大于x
的元素放在原数组的右半部分。其中有些细节需要注意。例如,算法中的下标i和j不会超
出 a门的下标界。另外,在快速排序算法中选取 aip]作为基准可以保证算法正常结束。如
果选择a印作为划分的基淮,且 al又是 alp:r中的最大元素,则 Partition 算法返回的值为
g=r,这会使 QuickSort 陷入死循环。
对于输入序列alp:门,Partition 算法的计算时间显然为 06-p-1。
快速排序的运行时间与划分是否对称有关,其最坏情况发生在划分过程产生的两个区域
分别包含 n-1 个元素和 1个元素的时候。由于函数 PartitionQ的计算时间为 0(0,所以如果
算法 Partition 的每步都出现这种不对称划分,则其计算时间复杂性 工の满足
(O(1)
n≤1
T(n)
=
IT(n-1) + O(n)
n>1
解此递归方程,可得Tn=O(m)。
在最好情況下,每次划分所取的基准都恰好为中值,即每次划分都产生两个大小为 n2
的区域,此时,Partition 算法的计算时间 T(满足
(0(1)
T(n)
=
(27 (n / 2) + O(n)
n<1
n>1
其解为 In)=O(nlogn)。
可以证明,快速排序算法在平均情况下的时问复杂性也是 O(mlogn),这在基于比较的排

3

谷易看到,快速排序算法的性能取次于划分的对称性。通过修改函数 Partiion0,田以
设计出采用随机选择筑咯的快速排序综法。在快速排序经法的每生中,当数组还没有被划分
时,可以在alon中随机选出;一个元素作为划分基准,这样可以使划分基准的选择是随机的,
从而可以期望划分是较对称的。随机化的划分算法可实现如下:

template<class Type>
int RandomizedPartition(Type a[],int p,int r)
{
    int i=Random(p,r);
    Swap(a[i],a[p]);
    return Partition(a,p,r);
}

4

其中,函数Random(p,工)产生p和r之间的一个随机整数,且产生不同整数的概率相同。随
机化的快速排序算法通过调用 RandomizedPartition 来产生随机的划分。

template<class Type>
{
    if(p<r)
        {
            int q=RandomizedPartition(a,p,r)
            RandomizedQuickSort(a,p,q-1);//对左半段排序
            RandomizedQuickSort(a,q+1,r);//对右半段排序
        }
}

我的理解:

快速排序是另一个分而治之的排序算法在继续随机化和可用版本的快速排序之前,我们将看到,这种确定性的,非随机化的快速排序的版本可能在针对性输入中具有很差的 O(N2) 时间复杂度。

我们首先讨论其最重要的子程序:O(N) 分区,然后解析这个快速排序算法。
要将 a[i..j] 分区,我们首先选择 a[i] 作为pivot p
将其余项目(即a[i+1..j])分为3个区域:

  1. S1 = a [i + 1..m]其中项目 ≤ p
  2. S2 = a [m + 1..k-1],其中项目 ≥ p,且
  3. 未知的= a [k..j],其中项目尚未分配给 S1 或 S2

讨论:为什么我们选择 p = a [i] 还有其他选择吗?

更难的讨论:如果 a[k] == p,我们应该把它放入 S1 还是 S2

最初,S1 和 S2 区域都是空的,即除了指定的pivot p 之外的所有项目都在未知区域中。
然后,对于未知区域中的每个项目 a [k],我们将 a[k] 与 进行比较, 并决定两种情况中的一个:

  1. 如果 a[k] > p,将 a[k] 放入 S2 中
  2. 如果 a[k] < p,将 a[k] 放入 S1 中
  3. 如果 a[k] == p,扔硬币决定将 a[k] 放入 S2 还是 S1 中。

接下来的两张幻灯片将会详细阐述这两种情况。
最后,我们交换 a[i] 和 a[m] 来将枢纽 放在 S1 和 S2 的中间。

对该算法的分析:

首先,我们分析一次分区的成本。
在函数 partition(a, i, j) 中,只有一个遍历了 j-i 次的 for 循环。 由于 j 可以和 N-1 一样大,i 可以低至0,所以分区的时间复杂度是O(N)。
类似归并排序分析,快速排序的时间复杂度取决于 partition(a, i, j) 被调用的次数。

当数组已经是升序的,如数组 a = [5, 18, 23, 39, 44, 50],Quick Sort 会令 p = a[0] = 5,并会返回 m = 0,这样就使得 S1 区域不含任何元素而 S2 区域涵盖除 pivot 外的 N-1 个元素。

 

 当分区总是将数组分成两个相等的一半时,就会发生快速排序的最佳情况,如归并排序
当发生这种情况时,递归的深度只有 O(log N)。
由于每个级别进行 O(N) 比较,时间复杂度为 O(N log N)。
在这个手工制作的示例输入数组 [4, 1, 3, 2, 6, 5, 7] 上尝试Quick Sort。这在实践中很少见,因此我们需要设计一个更好的方法:随机快速排序

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值