基本排序算法:快速排序算法及其优化

快速排序:就是在一个数组中先选取一个参考值V(基准),并将数组分为小于基准和不小于基准的两部分(快速排序的基本版),并递归将左右部分两部分继续分别快速排序,用到了分治的算法思想,将一个大问题分解成一系列有相同特点或性质的子问题,当子问题仍无法解决时继续递归划分,直到子问题可以被解决为止,这样只要将子问题一一解决,总的问题也就被解决了。

注意:代码涉及3个函数,需要从最下面一个开始往上看,它们是相互调用的关系。

快速排序:

// 对arr[l...r]部分进行partition操作
// 返回p,使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
template 
int __partition(T arr[], int l, int r){

    T v = arr[l];

    int j = l; // arr[l+1...j] < v ; arr[j+1...i) > v
    for( int i = l + 1 ; i <= r ; i ++ )
        if( arr[i] < v ){
            j ++;   //这里的j变量代表的是每次交换的大于V的元素的下标,而且j的大小也代表着在当前情况下已经出现了多少的比V小的元素
            swap( arr[j] , arr[i] );
        }

    swap( arr[l] , arr[j]);

    return j;
}

// 对arr[l...r]部分进行快速排序
template 
void __quickSort(T arr[], int l, int r){

    if( l >= r )
        return;

    int p = __partition(arr, l, r); //分成左右两区间,左边的都小于基准,右边的都不小于基准,函数返回记住的索引值
    __quickSort(arr, l, p-1 );      //递归对左右两部分进行快排
    __quickSort(arr, p+1, r);
}

template 
void quickSort(T arr[], int n){

    __quickSort(arr, 0, n-1);
}

基本的快速排序存在的问题是:当数组中有大量重复元素的时候,快排的效率较低,因为这个时候左右部分很不平衡。

快速排序(二路):

template 
int _partition2(T arr[], int l, int r){

    swap( arr[l] , arr[rand()%(r-l+1)+l] );
    T v = arr[l];

    // arr[l+1...i) <= v; arr(j...r] >= v
    int i = l+1, j = r;
    while( i<=j ){    //二路快速排序较一路的代码编写特点有一个是:二路快排用了两个循环整个数组的变量,即这里的i和j
        while( i <= r && arr[i] < v )  //退出这个循环的时候i停在不小于基准值V的位置上
            i ++;

        while( j >= l+1 && arr[j] > v )  退出这个循环的时候i停在不大于基准值V的位置上
            j --;

        swap( arr[i] , arr[j] );
        i ++;
        j --;
    }

    swap( arr[l] , arr[j]);  //交换arr[l]和arr[j]使arr[l]左边部分元素都小于基准值。

    return j; //i停在第一个不小于基准值V的元素的位置上,而j停在最后一个小于基准值V的元素的位置上。
}

template 
void _quickSort(T arr[], int l, int r){

    if( r - l <= 15 ){    //当元素数量只有15个的时候,这时候用插入排序的效率更高一些
        insertionSort(arr,l,r);
        return;
    }

    int p = _partition2(arr, l, r);
    _quickSort(arr, l, p-1 );
    _quickSort(arr, p+1, r);
}

template 
void quickSort(T arr[], int n){

    srand(time(NULL));
    _quickSort(arr, 0, n-1);
}

二路快速排序将相同值的元素尽量平分给了左右部分,使得对有大量重复元素的数组的排序效率有了较大的提高。

 

快速排序(三路):

基准V              小于V          等于V e    未遍历到           大于V
void __quickSort3Ways(T arr[], int l, int r){

    if( r - l <= 15 ){
        insertionSort(arr,l,r);
        return;
    }

    swap( arr[l], arr[rand()%(r-l+1)+l ] );  //选取基准的时候随机选取。

    T v = arr[l];

    int lt = l;     // arr[l+1...lt] < v ,lt的大小为最后一个小于基准值的元素下标值
    int gt = r + 1; // arr[gt...r] > v ,gt的大小为第一个大于基准值的元素的下标值    
    int i = l+1;    // arr[lt+1...i) == v ,i的大小始终为当前待判断的元素的下标值
    //在设定lt,gt,i的初始值的时候要先保证这时候v三个区间内不存在元素,因为这时候还没开始分出来,下面的while()循环才是选出元素的。
    while( i < gt ){
        if( arr[i] < v ){
            swap( arr[i], arr[lt+1]);
            i ++;
            lt ++;  //别忘了lt++
        }
        else if( arr[i] > v ){
            swap( arr[i], arr[gt-1]);
            gt --;    //这时候i不需要++,因为这时候交换过来的元素仍是未判断的元素
        }
        else{ // arr[i] == v
            i ++;   
        }
    }

    swap( arr[l] , arr[lt] );

    __quickSort3Ways(arr, l, lt-1);
    __quickSort3Ways(arr, gt, r);
}

template 
void quickSort3Ways(T arr[], int n){

    srand(time(NULL));
    __quickSort3Ways( arr, 0, n-1);
}

 

你可能感兴趣的:(寒假算法)