快速排序

【快排算法,源自算法导论】

PARTITION(A,p,r)
	x = A[r]
	i = p-1
	for j = p to r-1
		if A[j] <= x
			i = i+1
			exchange (A[i],A[j])
	exchange(A[i+1],A[r])
	return i+1

QUICKSORT(A,p,r)
	if p < r
		q = PARTITION(A,p,r)
		QUICKSORT(A,p,q-1)
		QUICKSORT(A,q+1,r)


【随机化版本】

就能降低遇到worst_case的概率了(而不是降低worst_case的时间复杂度)

PARTITION(A,p,r)
	x = A[r]
	i = p-1
	for j = p to r-1
		if A[j] <= x
			i = i+1
			exchange (A[i],A[j])
	exchange(A[i+1],A[r])
	return i+1

RANDOM_PARTITION(A,p,r)
	i = RANDOM(p,r)
	exchange(A[i],A[r])
	return PARTITION(A,p,r)

QUICKSORT(A,p,r)
	if p < r
		q = RANDOM_PARTITION(A,p,r)
		QUICKSORT(A,p,q-1)
		QUICKSORT(A,q+1,r)


【理解】

(1)这是个不稳定的排序,因为两个同样的值,可能排完序后顺序变了。

比如说1,2,3,2,5 ,为了讲解方便,我们令第一个2为2[1],第二个2为2[2],快排Partition()中如果我把2[1]当成x,让其与A[r]交换

程序往下走,然后遇到2[2],因为2[2] <= x ,那么 i++ , exchange(A[i],A[j] ),那么2[2]就跑到前面去了,循环结束后exchange(A[i+1],A[r])

把2[1]放到A[i+1]的位置,这个位置在2[2]后,你看,排完序,两个相同值可能顺序就变了。

(2)这是个最好只有O(nlogn)的排序,最坏的情况就是排完序的情况,为O(n^2)

它把一个序列划分两段,一共有logn次划分。

二分能够降低时间复杂度,O(n) 变成O(logn) ,O(n^2) 变成O(nlogn)

如果排完序呢,就没办法二分了,或者说二分效果不大,就没办法把其中一个n给降到logn,所以是n^2

【C++ 代码一】

这个是2013/4/5写的

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <math.h>
#include <time.h>
#include <cstdlib>

using namespace std;

const int N = 1000;
int Arr[N];

void exchange(int &a,int &b)
{
    if(a != b)
    {
        a=a+b;
        b=a-b;
        a=a-b;
    }
}

int Partition(int *A,int p,int r)
{
    try
    {
        int i=p-1;
        int x = A[r];
        for(int j=p;j<=r-1;j++)
            if(A[j] <= x)
                exchange(A[++i],A[j]);
        exchange(A[++i],A[r]);
        return i;
    }
    catch(...)
    {
        cout<<"extend the array index..."<<endl;
        return -1;
    }
}

int random_partition(int *A,int p,int r)
{
    int i=rand()%(r-p+1)+p;
    exchange(A[i],A[r]);
    return Partition(A,p,r);
}

void QuickSort(int *A,int p,int r)
{
    if(p<r)
    {
        int x = random_partition(A,p,r);
        QuickSort(A,p,x-1);
        QuickSort(A,x+1,r);
    }
}

int main()
{
    int num=5;
    srand((unsigned)time(NULL));
    for(int i=1;i<=num;i++)
        Arr[i] = rand()%100;

    cout<<"before quick sort:"<<endl;
    for(int i=1;i<=num;i++)
        cout<<Arr[i]<<" ";
    cout<<endl;

    QuickSort(Arr,1,num);

    cout<<"after quick sort:"<<endl;
    for(int i=1;i<=num;i++)
        cout<<Arr[i]<<" ";
    cout<<endl;

    return 0;
}
/*
实验结果:
before quick sort:
9 37 5 11 93
after quick sort:
5 9 11 37 93
*/


 

【C++代码二】

这个是2011写的,废话有点多。。

C++代码

//随机化版本会降低遇到worst_case的概率,而不是降低worst_case的时间复杂度
//code for testing QuickSort by ouyang

#include <iostream>
#include <time.h>   //time()函数需要
#include <stdlib.h> //srand()函数需要

using namespace std;

/*传址交换两个int型整数*/
void Exchange(int *a,int *b)
{
    int temp=*a;
    *a=*b;
    *b=temp;
}
/*产生随机数random_number*/
int Random(int p,int r)
{
    int random_number;
    srand((time(0)));
    if(p<r)
        random_number=rand()%(r-p+1)+p;
    return random_number;
}
/*对从p到r的p-r+1个数排序*/
int Partition(int *a,int p,int r)
{
    //这个函数是快排的精华所在,对p到r这r-p+1个数排序
    //它会划分三个区域:
    //1.小于x的(从头至i所指向处)
    //2.正在排的(j)
    //3.大于x的(从i+1至j-1所指向处)
    //如果j小于等于阈值x(也就是被选定的a[r]),那么a[j]和a[++i]交换后:
    //1.从头到i(注意i已经加1了)所指处,依旧保持小于x
    //2.从i+1到j-1(注意j在循环中已经加1了)所指处,依旧保持大于x
    //3.j依旧是正在排的
    //如果j大于阈值x,那么:
    //1.从头到i(i没有自加1)所指处依旧是小于x
    //2.从i+1到j-1(注意j在循环中已经加1了)依旧是大于x
    //3.j依旧是正在排的
    int i=p-1;
    int x=a[r];//阈值x
    for(int j=p; j<=r-1; j++) //因为阈值就是a[r],所以j==r-1即可
    {
        if(a[j]<=x)
        {
            i++;
            Exchange(&a[j],&a[i]);
        }
    }
    //和阈值交换之后,依旧保持着
    //1.从头到i处小于x
    //2.(i+1)+1到r-1都是大于x
    //3.i+1处就是阈值x
    Exchange(&a[i+1],&a[r]);

    return i+1;
}
/*Partition的随机化版本*/
int Randomized_Partition(int *A,int p,int r)
{
    int i=Random(p,r);
    Exchange(&A[r],&A[i]);
    return Partition(A,p,r);
}
/*快速排序*/
void QuickSort(int *a,int p,int r)
{
    if(p<r)
    {
        int q=Randomized_Partition(a,p,r); //q用来将序列分为三部分:(p,q),q,(q+1,r)
        QuickSort(a,p,q-1);
        QuickSort(a,q+1,r);
    }
}

int main()
{
    int a[10]= {NULL,11,22,33,44,15,226,7,8,9};
    QuickSort(a,1,9);
    for(int i=1; i<=9; i++)
        cout<<a[i]<<" ";
    cout<<endl;
    return 0;
}

【非递归版本】

2013/4/5,修改

非递归效率可能更高些。不过个人还是比较递归版本。

用stack保存每一段的首尾号low && high

然后取出一对并处理

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <math.h>
#include <time.h>
#include <cstdlib>
#include <stack>

using namespace std;

const int N = 1000;
int Arr[N];

int Partition(int *A,int low,int high)
{
    int x=A[low];
    while(low < high)
    {
        while(low < high && A[high]>=x)
            high--;
        A[low]=A[high];

        while(low < high && A[low]<x)
            low++;
        A[high]=A[low];
    }
    A[low]=x;
    return low;
}

void QuickSort_NonRecurrence(int* A,int low,int high)
{
    stack<int> s;
    if(low < high)
    {
        s.push(low);
        s.push(high);

        //实际上就是保存low high 这一对序列号
        //别把low high 弄错顺序了,先压人low,则先取出high
        while(!s.empty())
        {
            int q = s.top(); s.pop();//high
            int p = s.top(); s.pop();//low
            int pqmid = Partition(A,p,q);
            if(p < pqmid-1)
            {
                s.push(p);
                s.push(pqmid-1);
            }
            if(pqmid+1 <q)
            {
                s.push(pqmid+1);
                s.push(q);
            }
        }
    }
}

int main()
{
    int num=5;
    srand((unsigned)time(NULL));
    for(int i=1;i<=num;i++)
        Arr[i] = rand()%100;

    cout<<"before quick sort:"<<endl;
    for(int i=1;i<=num;i++)
        cout<<Arr[i]<<" ";
    cout<<endl;

    QuickSort_NonRecurrence(Arr,1,num);

    cout<<"after quick sort:"<<endl;
    for(int i=1;i<=num;i++)
        cout<<Arr[i]<<" ";
    cout<<endl;

    return 0;
}
/*
实验结果:
before quick sort:
76 15 99 30 43
after quick sort:
15 30 43 76 99
*/


 


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值