快速排序

 

 快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序n个元素要O(nlogn)次比较。在最坏状况下则需要O(n^2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他O(nlogn)算法更快,因为它的内部循环可以在大部分的架构上很有效率地被实现出来。

    每经过一趟快排,轴点元素都必然就位,也就是说,一趟下来至少有1个元素在其最终位置

  快速排序使用分治策略(Divide and Conquer)来把一个序列分为两个子序列。步骤为:

  1. 从序列中挑出一个元素,作为"基准"(pivot).
  2. 把所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基准的后面(相同的数可以到任一边),这个称为分区(partition)操作。
  3. 对每个分区递归地进行步骤1~2,递归的结束条件是序列的大小是0或1,这时整体已经被排好序了。
void quickSort(int a[],int left,int right)
{
    if(left>right) return;
    int i,j,t,temp;
    i=left;j=right;
    temp=a[left];  //temp为基准数,暂定将最左边的第一个元素设置为基准
    while(i!=j)
    {
        while(a[j]>=temp&&i<j) //顺序很重要!!!必须先从右边开始找
            j--;
        while(a[i]<=temp&&i<j) //再从左边开始找
            i++;
        if(i<j)       //交换两个数
        {
            t=a[i];
            a[i]=a[j];
            a[j]=t;
        }
    }
   a[left]=a[i];      //最终将基准数归位,将基准数调到该数组的大致中间位置
   a[i]=temp;
   quickSort(a,left,i-1); //继续递归处理左边的
   quickSort(a,i+1,right);//继续递归处理右边的
}
 
//快排思想,分区处理
int Partition(int a[],int left,int right)
{
    if(left>right) return 0;
    int i,j,t,temp;
    i=left;j=right;
    temp=a[left];  //temp为基准数,暂定将最左边的第一个元素设置为基准

    while(i!=j)
    {
        while(a[j]>=temp&&i<j) //顺序很重要!!!必须先从右边开始找
            j--;
        while(a[i]<=temp&&i<j) //再从左边开始找
            i++;
        if(i<j)       //交换两个数
        {
            t=a[i];
            a[i]=a[j];
            a[j]=t;
        }

    }
   a[left]=a[i];      //最终将基准数归位,将基准数调到该数组的大致中间位置
   a[i]=temp;
   return i;          //返回第i的大的索引

}
//求top k数字
int FindKMax(int a[],int start,int end,int k)
{
    int q;
    int index=-1;
    if(start < end)
    {
        q = Partition(a , start , end);
        int len = q - start + 1; //表示第几个位置
        if(len == k)
            index=q;           //返回第k个位置
        else if(len < k)
            index= FindKMax(a , q + 1 , end , k-len);  //左边数字小于K个,则去右边寻找第k-len个
        else
            index=FindKMax(a , start , q - 1, k);         //左边数字大于K个,则继续去左边寻找第k个
    }
    return index;

}
//快速排序
void QuickSort(int a[],int start,int end)
{
    if(start==end)
        return;
    int index=Partition(a,start,end);
    if(index>start)
        QuickSort(a,start,index-1);
    if(index<end)
        QuickSort(a,index+1,end);
}

 

int main(int argc, char *argv[])
{     
    //返回top k数字
    int a[]={20,100,4,2,87,9,8,5,46,26};
    int Len=sizeof(a)/sizeof(int);
    int K=4;
    FindKMax(a , 0 , Len- 1 , K) ;
    for(int i = 0 ; i < K ; i++)
        cout<<a[i]<<" ";
    return 0;
}

    http://blog.youkuaiyun.com/vayne_xiao/article/details/53508973

 快排的优化

  选择基准的方式: 
  对于分治算法,当每次划分时,算法若都能分成两个等长的子序列时,那么分治算法效率会达到最大。也就是说,基准的选择  是很重要的。选择基准的方式决定了两个分割后两个子序列的长度,进而对整个算法的效率产生决定性影响

 最理想的方法是,选择的基准恰好能把待排序序列分成两个等长的子序列  

固定选取基准 

 基本的快速排序选取第一个或最后一个元素作为基准(固定选取如前面代码所示)。但是,这是一直很不好的处理方法。

    è¿éåå¾çæè¿°

    测试数据分析:如果输入序列是随机的,处理时间可以接受的。如果数组已经有序时,此时的分割就是一个非常不好的分割。因为每次划分只能使待排序序列减一(不是理想中的等长子序列),此时为最坏情况,快速排序沦为起泡排序,时间复杂度为Θ(n^2)。而且,输入的数据是有序或部分有序的情况是相当常见的。因此,使用第一个元素作为枢纽元是非常糟糕的,为了避免这个情况,就引入了下面获取基准的方法。

  随机选取基准

  引入的原因:在待排序列是部分有序时,固定选取枢轴使快排效率底下,要缓解这种情况,就引入了随机选取枢轴

  思想:取待排序列中任意一个元素作为基准

  随机化算法

/*随机选择枢轴的位置,区间在low和high之间*/  
int SelectPivotRandom(int arr[],int low,int high)  
{  
    //产生枢轴的位置  
    srand((unsigned)time(NULL));  
    int pivotPos = rand()%(high - low) + low;  

    //把枢轴位置的元素和low位置元素互换,此时可以和普通的快排一样调用划分函数  
    swap(arr[pivotPos],arr[low]);  
    return arr[low];  
}  

      è¿éåå¾çæè¿° 

 测试数据分析::这是一种相对安全的策略。由于枢轴的位置是随机的,那么产生的分割也不会总是会出现劣质的分割。在整个数组数字全相等时,仍然是最坏情况,时间复杂度是O(n^2)。实际上,随机化快速排序得到理论最坏情况的可能性仅为1/(2^n)。所以随机化快速排序可以对于绝大多数输入数据达到O(nlogn)的期望时间复杂度。一位前辈做出了一个精辟的总结:“随机化快速排序可以满足一个人一辈子的人品需求。” 

转载链接:最全的优化方案:https://blog.youkuaiyun.com/hacker00011000/article/details/52176100

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值