高级排序算法之快速排序

算法分析

快速排序算法的时间复杂度为nlog(n)。

基本思想:选择一个元素作为标志,比如下标为k的元素,经过排序使,arr[0,1,2....k-1]的元素小于arr[k],arr[k+1,k+2...n]的元素大于arr[k],然后对arr[0,1,2...k-1]和arr[k+1,k+2...n]两部分元素进行之前类似的操作。

算法代码示例如下:

template<typename T>
int _partition(T arr[], int l, int r)
{
    int v = arr[l];

    int j = l;      //arr[l+1...j] < v ; arr[j+1...r] > v
    for(int i = l + 1; i <= r; i++)
        if(v > arr[i])
            swap(arr[++j], arr[i]);   

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

template<typename T>
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<typename T>
void quickSort(T arr[], int n)
{
    _quickSort(arr, 0, n-1);
}

快速排序算法与插入排序、归并排序性能更好,但是对于几乎有序的数据,以上的快速排序性能会很差,时间复杂度可能会接近O(n^2)。

 算法优化

针对以上的快速排序算法,对于有序的数据再进行排序,由于每次都选择第一个元素作为标志后,出现数据量一边倒的情况,导致快速排序的性能很差,时间复杂度为O(n^2),以下通过随机选择标志元素的方式避免这种情况,以下为优化过后的代码:

template<typename T>
int _partition(T arr[], int l, int r)
{
    //优化点2:通过随机选择元素标志,防止对几乎有序的数据排序慢的问题
    srand(time(NULL));
    swap(arr[l], arr[rand()%(r-l+1)+l]);
    
    int v = arr[l];

    int j = l;      //arr[l+1...j] < v ; arr[j+1...r] > v
    for(int i = l + 1; i <= r; i++)
        if(v > arr[i])
            swap(arr[++j], arr[i]);   

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

template<typename T>
void _quickSort(T arr[], int l, int r)
{
    //if(l >= r)
    //    return ;
    //优化点1:小规模数据使用插入排序
    if(r-l <= 15)
    {
        insertionSort(arr, l, r);
        return;
    }

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

template<typename T>
void quickSort(T arr[], int n)
{
    _quickSort(arr, 0, n-1);
}

 经过优化以后,对于几乎有序的数据进行排序性能得到了提升,但是对于具有大量重复的数据排序,选择标志数据为v后,可能会出现大量等于v的数据,如下图所示,会出现排序的数据一边倒的情况,排序性能会很差。接下来我们继续对快速排序算法进行优化,以下优化过后的排序算法会有个新的名字,叫双路快速排序

 

posted @ 2019-04-07 18:11  心梦无痕bhl  阅读(230)  评论(0编辑  收藏  举报