【排序算法】:快速排序

介绍

快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

三数取中

我们在 进行快速排序时需要把数据划分为两个部分,需要一个key值。为了避免极端情况(key值是最大或最小值–>划分的两个部分就变成了,一部分数最大最小值,一部分是剩下的元素),我们可以利用三数取中的办法,挑选一个中间值。

int GetMidIndex(int* arr, int left, int right)//三数取中法
{
    assert(arr);
    assert(left < right);

    int mid = left + (right - left) / 2;

    if (arr[left] < arr[mid])
    {
        if (arr[mid] < arr[right])
            return mid;
        else if (arr[left] > arr[right])
            return left;
        else
            return right;;
    }
    else           //arr[left] > arr[mid]
    {
        if (arr[mid] > arr[right])
            return mid;
        else if (arr[right] > arr[left])
            return left;
        else
            return right;
    }
}

我们可以用三种方法实现快速排序。

左右指针法

有一个数组a[size],左指针指向下标0的位置,右指针right指向下标size-1的位置。同时选择一个key,key=a[right];left找a[left] > key的下标,right找a[right] < key的下标。left先开始走,当遇到比key大的数时,停下来。right开始往前走,找到比key小的数时,停下来。将left和right分别指向的元素交换。
但是,此时应保证left < right,如果left >= right就说明一次快排结束了、再将left指向的值与key值交换即可。此时的数组呈现出左边的值都比key值小,右边的值都比key值大。一次快排将数组分为两个区间,我们再对每个区间进行上述的排序方式。直到每个小区间已不能再划分。

原理图:

这里写图片描述

代码实现

//左右指针法
int PartSort1(int* arr, int left, int right)
{
    assert(arr);
    assert(right > left);

    int mid = GetMidIndex(arr, left, right);//三数取中
    swap(arr[mid], arr[right]);
    int key = arr[right];
    int begin = left;
    int end = right;

    while (begin < end)
    {
        while (begin < end && arr[begin] <= key)//找大
            ++begin;

        while (begin < end && arr[end] >= key)//找小
            --end;

        if (begin < end)
            swap(arr[begin], arr[end]);
    }
    swap(arr[begin], arr[right]);
    return begin;
}

挖坑法

有一个数组a[size],左指针left指向下标为0的位置,右指针right指向下标为size-1的位置,同时标志key=a[right];首先将key的位置设为一个坑,从left开始,找比key值大的,找到后将a[left]放到坑里,即a[right]=a[left],同时将left的位置再设为坑;再从right开始找比key小的,找到后将a[right]放到上次设的坑里,即a[left]= a[right];直到left>=right,将key值放到现在的坑中即可。此时的数组呈现出左边的值都比key值小,右边的值都比key值大。一次快排将数组分为两个区间,我们再对每个区间进行上述的排序方式。直到每个小区间已不能再划分。

原理图

这里写图片描述

代码实现

//挖坑法
int PartSort2(int *arr, int left, int right)
{
    assert(arr);
    assert(left < right);

    int key = arr[right];
    while (left < right)
    {
        while (left < right && arr[left] <= key)
            ++left;
        arr[right] = arr[left];

        while (left < right && arr[right] >= key)
            --right;
        arr[left] = arr[right];
    }

    arr[left] = key;
    return left;
}

前后指针法

定义两个指针,prev和cur,在定义key的值,cur从left开始往后走,遇到直到比key小的值,prev++。判断prev和cur是否相等。如果不相等,就讲两个数进行交换。然后继续进行上述步骤。最后一趟排序下来,比key小的,都留在了key的左边,比key大的都在key的右边。

原理图

这里写图片描述

代码实现

//前后指针法
int PartSort3(int* arr, int left, int right)
{
    assert(arr);
    assert(left < right);

    int mid = GetMidIndex(arr, left, right);
    swap(arr[mid], arr[right]);

    int cur = left;
    int prev = left - 1;
    int key = arr[right];

    while (cur < right)
    {
        if (arr[cur] < key && ++prev != cur)
            swap(arr[cur], arr[prev]);
        ++cur;
    }
    swap(arr[++prev], arr[right]);
    return prev;
}

非递归实现快速排序

//非递归实现快速排序
void QuickSortNoR(int* a, int left, int right)
{
    stack <int> s;
    s.push(right);
    s.push(left);

    while (!s.empty())
    {
        int begin = s.top();
        s.pop();
        int end = s.top();
        s.pop();

        int div = PartSort3(a,begin,end);
        if (div - 1 > begin)
        {
            s.push(div - 1);
            s.push(begin);
        }
        if (div + 1 < end)
        {
            s.push(end);
            s.push(div + 1);
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值