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


Partition1:(传统法)

定义一个标准key,此处key选择数组的最后面a[right],然后定义begin和end,分别从前往后找比key大的和从后往前找比key小的,找到就进行交换,最后将数组变为以key为界的两个数组,进行递归

int Partition1(int *a, int left, int right)
{
    assert(a);
    int begin = left, end = right - 1, key = a[right];
    while (begin < end)
    {
        //找比key大的
        while (a[begin] <= key && begin < end)
        {
            begin++;
        }
        //找比key小的
        while (a[end] >= key && end > begin)
        {
            --end;
        }
        //找到了进行交换
        if (begin < end)
        {
            swap(a[begin], a[end]);
        }
    }
    //将key放在两个数组之间
    if (a[begin] > a[right])
    {
        swap(a[begin], a[right]);
        return begin;
    }
    //只进行left—>right-1的递归
    else
    {
        return right;
    }
}
void QuickSort1(int *a, int left, int right)
{
    assert(a);
    if (right > left)
    {
        int boundary = Partition1(a, left, right);
        QuickSort1(a, left, boundary - 1);
        QuickSort1(a, boundary + 1, right);
    }
}

Partition2:

依然是在数组中找一个标准key=a[right],定义prev和cur(初始cur - 1 = prev),分别向前指向比key小的和比key大的,逐次进行交换,直到数组最后面。    

int Partition2(int *a, int left, int right)
{
    assert(a);
    int cur = left, prev = left - 1, key = a[right];
    while (cur < right)
    {
    //a[cur]<key时,prev+1,进行交换
        if (a[cur] < key && (++prev) != cur)
        {
            swap(a[prev], a[cur]);
        }
        ++cur;
    }
    //最终将数组分成以key为界的两个数组
    swap(a[++prev], a[right]);
    return prev;
}
void QuickSort2(int *a, int left, int right)
{
    assert(a);
    if (right > left)
    {
        int boundary = Partition2(a, left, right);
        QuickSort2(a, left, boundary - 1);
        QuickSort2(a, boundary + 1, right);
    }
}

Partition3:挖坑补位法

定义key=a[right] (任意),此时a[right]就相当于是一个没有数据的坑,begin(初始)从前往后找比key大的,找到后就将该数据填充到a[right]中,此时begin位置就变成了坑,end从后往前找小的,找到了就填充到上一个坑,最终当begin=end时,将key放进去,数组又变成了以key 为界的两个数组。

int Partition3(int* a, size_t left, size_t right)
{
    assert(a);

    int begin = left, end = right, key = a[end];
    while (begin < end)
    {
        //begin向前找比key大的
        while (begin < end && a[begin] <= key)
        {
            begin++;
        }
        //填坑
        if (begin < end)
        {
            a[end] = a[begin];
        }
        //end从后向前找比key小的
        while (end > begin && a[end] >= key)
        {
            end--;
        }
        //填坑
        if (end > begin)
        {
            a[begin] = a[end];
        }
    }
    //最终将数组分成以key为界的两个数组
    a[begin] = key;
    return begin;
}

void QuickSort3(int *a, int left, int right)
{
    assert(a);

    if (right > left)
    {
        int boundary = Partition3(a, left, right);
        QuickSort3(a, left, boundary - 1);
        QuickSort3(a, boundary + 1, right);
    }
}

优化法:(三数取中法)

当key为最大最小时,递归次数明显变大(效率低),所以尽量将key设置适中。

int GetMidIndex(int* a, size_t left, size_t right)
{
    int mid = left + (right - 1);

    if (a[left] < a[right])
    {
        if (a[mid] < a[left])
            return left;
        else if (a[mid] < a[right])
            return mid;
        else
            return right;
    }
    else
    {
        if (a[mid] < a[right])
            return right; 
        if (a[mid] < a[left])
            return left;
        else
            return mid;
    }
}

非递归实现QuickSort

void QuickSort4(int* a, size_t left, size_t right)
{
    assert(a);

    stack<int> s;
    if (left < right)
    {
        int boundary = Partition3(a, left, right);

        if (left < boundary - 1)
        {
            s.push(left);
            s.push(boundary - 1);
        }
        if (boundary + 1 < right)
        {
            s.push(boundary + 1);
            s.push(right);
        }
        while (!s.empty())
        {
            int end = s.top();
            s.pop();
            int begin = s.top();
            s.pop();
            boundary = Partition2(a, begin, end);

            if (begin < boundary - 1)
            {
                //左边界在下,右边界在上。
                s.push(begin);
                s.push(boundary - 1);
            }
            if (boundary + 1 < end)
            {
                s.push(boundary + 1);
                s.push(end);
            }
        }
    }
}