算法导论(四)--快排和随机化算法

算法导论(四)--快排和随机化算法

快速排序

算法:
快速排序是一个分治算法,而且就在原地排序,和插入排序一样,比较节省内存。而归并排序需要额外的空间来进行归并,为了在线性时间和空间内归并。
分治步骤:先选一个数,根据它的大小把原数组分为两个子数组,一个都小于这个数,一个都大于等于这个数,然后递归处理两个子数组的排序,最后把它们连接起来。
快速排序里最关键的一步就是划分(Partition),可以把算法看成是递归地划分数组,就像归并排序是递归地归并数组,但快速排序的复杂度是线性的。

Partition伪代码:

Partition(A,p,q) //处理数组的p到q部分A[p,...,q]
{
   
x=A[p];//选一个主元数据pivot=A[p]
i=p;
for j=p+1 to q
	do if A[j]≤x
		then i++;
			 exch A[i]<->A[j]
exch A[p]<->A[i]
return;
}

在这里插入图片描述
看这个图理解一下上面的伪代码,p到i都小于x,i+1到j都大于等于x。这段代码非常漂亮,T(n)=Θ(n)。

举例:
初始x=6,i指向6,j指向10。
6 10 13 5 8 3 2 11 | x=6
 i    j
j向右扫描,直到发现5比6小,就把i++,5和10交换,得到:
6 5 13 10 8 3 2 11
    i             j
这时候i指向5,j指向8。
继续扫描,发现3比6小,于是i++,把3和13交换,得到:
6 5 3 10 8 13 2 11
        i               j
这时候i指向3,j指向2。
继续扫描,发现2比6小,i++,把2和10交换,得到:
6 5 3 2 8 13 10 11
           i                j
这时候i指向2,j指向11。j=q,结束循环。
最后把A[p]和A[i]交换,得到:
2 5 3 6 8 13 10 11
这一次partition就完成了,结果就是6左边的数都小于6,右边的数都大于等于6。下一次partition就以2为主元。

快速排序伪代码:

Quicksort(A,p,q)
{
   
if p<q
	then r=Partition(A,p,q);
		 Quicksort(A,p,r-1);
		 Quicksort(A,r+1,q);
}
//调用
Quicksort(A,0,n-1);

下面是另一种写法,i和j在两边,向中间移动,直到相遇:

int Partition(int* arr,int l,int r)
{
   
    int i=l,j=r;
    int tmp=arr[i];
    while(i<j)
    {
   
        while(i<j && arr[j]>tmp)
            j--;
        if(i<j)
        {
   
            arr[i]=arr[j];
            i++;
        }
        while(i<j && arr[i]<tmp)
            i++;
        if(i<j)
        {
   
            arr[j]=arr[i];
            j--;
        }
    }
    arr[i]=tmp;
    return i;
}
void Quicksort(int* arr,int l,int r)
{
   
    if(l<r)
    {
   
        int i=Partition(arr, l, r);
        Quicksort(arr, l, i-1);
        Quicksort(arr, i+1, r);
    }
    
}

分析:
考虑第一种方法,如果每次选一个主元,其它的数都比它大或者小,就是最坏的情况,即数组基本有序。因为每个分划的一边都没有元素。这种情况下,一边没有元素,一边有n-1个元素,T(n)=T(0)+T(n-1)+Θ(n),其中T(0)=Θ(1),所以T(n)=T(n-1)+Θ(n)=Θ(n2),可以用递归树法求出。不比插入排序快。
最好情况下,分划总是发生在n/2处,那么T(n)=2T(n/2)+Θ(n)=Θ(nlogn)。如果分划总是发生在1/10:9/10处呢,那么T(n)=T(n/10)+T(9n/10)+Θ(n)=Θ(nlogn),是一样的。
那么平均呢?也就是好的情况与不好的情况交替发生,那么设L(n)=2U(n/2)+Θ(n),U(n)=L(n-1)+Θ(n),代换一下就是L(n)=2L(n/2-1)+2Θ(n/2)+Θ(n)=2L(n/2-1)+Θ(n)=Θ(nlogn)。(用主方法)

随机化快速排序

随机化快速排序就是随机地选主元,在分划之前,把首元素与序列中的其他一个元素交换,任何元素都可能成为主元,不是像之前那样把第一个元素作为主元。这样运行时间不依赖于输入序列的顺序,即无需对输入的数据做任何假设,并且没有一种特定的输入会引起最差的运行效率,最差的情况由随机数产生器决定。

分析:
下面是一些数学上的分析。令T(n)为运行时间的随机变量,这些随机数是独立的,即选择主元的事件是独立的。为了便于分析,我们需要知道哪个为主元。
设k=0,1,…,n-1,
X k = { 1 当 产 生 一 个 k : ( n − k − 1 ) 分 划 0 其 他 X_k = \begin{cases} 1 &amp; 当产生一个k : (n-k-1)分划 \\ 0 &amp; 其他 \end{cases} Xk={ 10k:(nk1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值