快速排序 原理与实现

参考一
  public void quicksort(int n[], int left,int right){
   int dp;
   if (left
   
   dp=partition(n,left,right);   
   quicksort(n,left,dp-1);  
   quicksort(n,dp+1,right);
   System.out.println(n[dp]+",");
   //这两个就是递归调用,分别整理53左边的数组和右边的数组}}     我们上面提到先定位第一个数,然后整理这个数组,把比这个数小的放到它的左边,大的放右边,然后返回这中间值的位置,下面这函数就是做这个的。
   
    }
   }
  public int partition(int n[],int left,int right){
   int lo,hi,pivot,t;
   pivot=n[left];
   lo=left-1;
   hi=right+1;
   while(lo+1!=hi) {    
    if(n[lo+1]<=pivot)       lo++;    
    else if(n[hi-1]>pivot)       hi--;   
    else {     
     t=n[lo+1];     
     n[++lo]=n[hi-1];     
     n[--hi]=t;    
    }
    }
      n[left]=n[lo];
      n[lo]=pivot;
      return lo;
      }

 

快速排序是对冒泡排序的一种改进。它的基本思想是:通过一躺排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一不部分的所有数据都要小,然后再按次方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

    假设要排序的数组是A[1]……A[N],首先任意选取一个数据(通常选用第一个数据)作为关键数据,然后将所有比它的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一躺快速排序。一躺快速排序的算法是:

   1)、设置两个变量I、J,排序开始的时候I:=1,J:=N;

   2)以第一个数组元素作为关键数据,赋值给X,即X:=A[1];

   3)、从J开始向前搜索,即由后开始向前搜索(J:=J-1),找到第一个小于X的值,两者交换;

   4)、从I开始向后搜索,即由前开始向后搜索(I:=I+1),找到第一个大于X的值,两者交换;

   5)、重复第3、4步,直到I=J;

参考二
快速排序(QuickSort)也是一种排序算法,对包含n个数组的输入数组,最坏情况运行时间为O(n^2)。虽然这个最坏情况运行时间比较差,但是快速排序通常是用于排序的最佳实用选择,这是因为其平均性能相当好,期望的运行时间为O(nlgn),且O(nlgn)中隐含的常数因子很小,另外它还能够进行就地排序在虚拟环境中也能很好的工作。
一、快速排序原理
       快速排序也和合并排序一样,基于分治法,分为分解、解决、合并三个步骤;
       分解:数组array[low...high]被分为两个(可能空)子数组array[low...temp-1]和array[temp+1...high],使得array[low...temp-1]中的每一个元素都小于等于array[temp],而array[temp+1...high]中的每一个元素都大于array[temp],下标temp也是在这个过程中被计算出来;
       解决:通过递归的调用快速排序,对子数组array[low...temp-1],array[temp+1...high]进行排序;
       合并:因为两个子数组是就地排序的,将他们的合并不需要操作,整个数组array[low...high]是已经排好序的。
二、快速排序实现
[cpp] 
#include <iostream>  
#include <ctime>  
#include <cstdlib>  
#define N 10  
  
using namespace std;  
  
//快速排序的递归算法  
void quickSort(int * array , int low , int high);  
//求分割点  
int partition(int * array , int low , int high);  
//交换两个变量的值  
void exchange(int &a,int &b);  
  
int main()  
{  
    //声明一个待排序数组     
    int array[N];    
    //设置随机化种子,避免每次产生相同的随机数      
    srand(time(0));     
    for(int i=0 ; i<N ; i++)      
    {      
        array[i] = rand()%101;//数组赋值使用随机函数产生1-100之间的随机数        
    }      
    cout<<"排序前:"<<endl;      
    for(int j=0;j<N;j++)      
    {      
        cout<<array[j]<<"  ";      
    }     
    cout<<endl<<"排序后:"<<endl;      
    //调用快速排序函数对该数组进行排序        
    quickSort(array,0,N-1);     
    for(int k=0;k<N;k++)      
    {      
        cout<<array[k]<<"  ";      
    }      
    cout<<endl;      
    return 0;      
}//main  
  
void quickSort(int * array , int low , int high)  
{  
    if(low < high)  
    {  
        int temp = partition(array , low , high);  
        quickSort(array , low , temp-1);  
        quickSort(array , temp+1 , high);  
    }  
}  
  
int partition(int * array , int low , int high)  
{  
    int i = low - 1;  
    int x = array[high];  
  
    for(int j=low ; j<high ; j++)  
    {  
        if(array[j] <= x)//在array[i]左边都是小于x即array[high]的数,右边均是大于它的数  
        {  
            i += 1;  
            exchange(array[i],array[j]);  
        }  
    }  
    exchange(array[i+1],array[high]);  
    return i+1;//所以循环完毕后,i+1就是该数组的分割点  
}  
void exchange(int &a,int &b)  
{   www.2cto.com
    int temp = a;  
    a = b;  
    b = temp;  
}  
运行结果:
 
三、快速排序性能分析
       快速排序的运行时间与划分是否对称有关,而后者又与选择了哪一个元素进行划分有关。如果划分是对称的,那么本算法在渐近意义上与合并排序一样快,如果划分是不对称的那么本算法在渐进意义上与插入排序一样慢。下面分别讨论快速排序的最坏情况划分、最家情况划分、平衡的划分。
       最坏情况划分:快速排序的最坏情况划分行为发生在划分过程中产生的两个区域分别包含n-1个元素和0个元素的时候。假设算法每次递归调用都出现了这种不对称划分,划分的时间代价为O(n),因为对一个大小为0的数组进行递归调用后,返回了T(n)=O(1),故算法的运行时间可递归的表示为:
                                                           T(n) = T(n-1) + T(0) + O(n) = T(n-1) + O(n)
       从直观上来看,如果将每一层递归的代价加起来,就可以得到一个算术级数(等式(array,2)其和值的量极为O(n^2))利用代换法可以比较直接的证明递归式 T(n) = T(n-1) + O(n)的解为 T(n) = O(n^2)。
       因此如果在算法的每一层递归上,划分都是最大程度不对称的,那么算法的运行时间为O(n^2),亦即快速排序算法的最坏情况运行时间不如插入排序的好。此外当输入数组完全排好序时,快速排序的运行时间是O(n^2),而插入排序的运行时间为O(n)。
       最佳情况划分:在Partition可能做的最平衡划分中,得到的两个子问题的大小都不可能大于[n/2],因为若其中一个子问题的大小为[n/2],则另外一个子问题的大小必然为[n/2]-1。在这种情况下,快速排序的运行速度要快得多,这时表达其运行时间的递归式为:
                                                         T(n) <= 2T(n/2) + O(n)     
       解该递归式可得T(n) = O(nlgn)。由于在每一层递归划分的两边都是对称的,因此从渐进意义上来看,算法运行的就更快了。
       平衡的划分: 快速排序的平均情况运行时间与其最佳情况运行时间很接近,而不是非常接近与其最坏情况运行时间(证明原因详细参考《算法导论》原书第二版P88),因为任何一种按常数比例进行划分都会产生深度为O(lgn)的递归树,其中每一层的代价都是O(n),因而每当按照常数比例进行划分时,总的运行时间都是O(nlgn)。
 
参考三
一般来说,冒泡法是程序员最先接触的排序方法,它的优点是原理简单,编程实现容易,但它的缺点就是--程序的大忌--速度太慢。下面我介绍一个理解上简单但编程实现上不是太容易的排序方法,我不知道它是不是现有排序方法中最快的,但也不错。排序同样的数组,它所需的时间只有冒泡法的 4% 左右。我暂时称它为“快速排序法”。
    “快速排序法”使用的是递归原理,下面我结合一个例子来说明“快速排序法”的原理。首先给出一个数组{53,12,98,63,18,72,80,46, 32,21},先找到第一个数--53,把它作为中间值,也就是说,要把53放在一个位置,使得它左边的值比它小,右边的值比它大。{21,12,32, 46,18,53,80,72,63,98},这样一个数组的排序就变成了两个小数组的排序--53左边的数组和53右边的数组,而这两个数组继续用同样的方式继续下去,一直到顺序完全正确。
    我这样讲你们是不是很胡涂,不要紧,我下面给出实现的两个函数:
/*
n就是需要排序的数组,left和right是你需要排序的左界和右界,
如果要排序上面那个数组,那么left和right分别是0和9
*/
void quicksort(int n[], int left,int right)
{
int dp;
if (left<right) {
    /*
    这就是下面要讲到的函数,按照上面所说的,就是把所有小于53的数放
    到它的左边,大的放在右边,然后返回53在整理过的数组中的位置。
    */
    dp=partition(n,left,right);
    quicksort(n,left,dp-1);
    quicksort(n,dp+1,right); //这两个就是递归调用,分别整理53左边的数组和右边的数组
}
}
    我们上面提到先定位第一个数,然后整理这个数组,把比这个数小的放到它的左边,大的放右边,然后
返回这中间值的位置,下面这函数就是做这个的。
int partition(int n[],int left,int right)
{
int lo,hi,pivot,t;
pivot=n[left];
lo=left-1;
hi=right+1;
while(lo+1!=hi) {
    if(n[lo+1]<=pivot)
      lo++;
    else if(n[hi-1]>pivot)
      hi--;
    else {
      t=n[lo+1];
      n[++lo]=n[hi-1];
      n[--hi]=t;
    }
}
n[left]=n[lo];
n[lo]=pivot;
return lo;
}
    这段程序并不难,应该很好看懂,我把过程大致讲一下,首先你的脑子里先浮现一个数组和三个指针,第一个指针称为p指针,在整个过程结束之前它牢牢的指向第一个数,第二个指针和第三个指针分别为lo指针和hi指针,分别指向最左边的值和最右边的值。lo指针和hi指针从两边同时向中间逼近,在逼近的过程中不停的与p指针的值比较,如果lo指针的值比p指针的值小,lo++,还小还++,再小再++,直到碰到一个大于p指针的值,这时视线转移到hi指针,如果 hi指针的值比p指针的值大,hi--,还大还--,再大再--,直到碰到一个小于p指针的值。这时就把lo指针的值和hi指针的值做一个调换。持续这过程直到两个指针碰面,这时把p指针的值和碰面的值做一个调换,然后返回p指针新的位置。
    我知道我怎么说你们还是不太明白,其实只要看程序就行了,有很多程序的语言是用人类的语言无法表达的。有什么问题,email我: allexit@hotmail.com
 
----------another------------
 
#include"stdio.h"
void quickSort(int a[],int left,int right)
{
   int i,j,temp;
   i=left;
   j=right;
   temp=a[left];
   if(left>right)
      return;
   while(i!=j)/*找到最终位置*/
   {
      while(a[j]>=temp && j>i)
         j--;
      if(j>i)
         a[i++]=a[j];
       while(a[i]<=temp && j>i)
          i++;
       if(j>i)
          a[j--]=a[i];
        
   }
   a[i]=temp;
   quickSort(a,left,i-1);/*递归左边*/
   quickSort(a,i+1,right);/*递归右边*/
}
void main()
{
    int a[7]={8,2,6,12,1,9,5};
    int i;
    quickSort(a,0,6);
    /*排好序的结果*/
    for(i=0;i<7;i++)
        printf("%4d",a[i]);
}
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值