快速排序

来源:互联网 发布:淘宝更改发货地址 编辑:程序博客网 时间:2024/06/05 19:34

一、快排的几种实现方法:
1.左右指针法:
a.算法思想:选定数组中最后一个数为关键值,从左往右遍历当遇到比关键值大的数就停下,
从右往左遍历遇到比关键值小的数就停下,交换两个数,直到当左右相遇,将关键值与指针所在
的位置互换,一轮过后关键值前面的数都比它小,关键值后的数都比它大。此时key将这段区间
划分成两半,然后依次对这两半区间排序,直到每个区间就剩一个数,那么就排好序了。
这里写图片描述

b.时间复杂度:快排每次都会划分区间,直到区间有一个数,这点类似于二叉树,单趟排序交换数的
过程大概走了n步,递归了lg n层,所以时间复杂度为o(n*lg n);
c.空间复杂度:递归了高度次所以空间复杂度为o(lg n);
d.代码实现:

int PartSort(int *a, int left, int right){    int key = right; //最后一个下标为基准值    while (left < right)    {        while (left < right&&a[left] <= a[key])        {            ++left;        }        while (left < right&&a[right] >= a[key])        {            --right;        }        if (left<right)            swap(a[left], a[right]);    }    swap(a[left], a[key]);    return left;}void QuickSortR(int *a, int left,int right){    int mid;    if(left < right)    {        mid = PartSort(a, left, right);        QuickSortR(a, left, mid - 1);        QuickSortR(a, mid + 1, right);    }}void PrintArray(int *a, size_t N){    for (size_t i = 0; i < N; i++)    {        cout << a[i] << " ";    }    cout << endl;}void TestQuickSort(){    int a[] = { 2, 0, 4, 9, 3, 6, 8, 7, 1, 5 };    size_t length = sizeof(a) / sizeof(a[0]);    QuickSortR(a, 0, (length - 1));    PrintArray(a, length);}

2.挖坑法:
a.算法思想:挖坑法和左右指针法原理类似,也是每次选取最后一个数为基准值,把这个数拿出来存放在
tmp中,然后这个位置就相当于一个坑,然后从前到后遍历,如果大于tmp就把这个数填到坑中,然后
这个位置就变成了新的坑,再从后往前遍历如果比tmp小,就把这个数填在刚才的坑中,直到left=right,
把最后的数填在left中。
这里写图片描述
b.时间复杂度:o(n*lg n);
c.空间复杂度:o(lg n);
e.代码实现:

//挖坑法int PartSort(int* a, int left, int right){    int tmp = a[right]; //记录最后一个位置    while (left < right)    {        while (left < right&&a[left] <= tmp)        {            ++left;        }        a[right] = a[left];        while (left < right&&a[right] >= tmp)        {            --right;        }        a[left] = a[right];    }    a[left] = tmp;    return left;}void QuickSortR(int *a, int left,int right){    int mid;    if(left < right)    {        mid = PartSort(a, left, right);        QuickSortR(a, left, mid - 1);        QuickSortR(a, mid + 1, right);    }}void PrintArray(int *a, size_t N){    for (size_t i = 0; i < N; i++)    {        cout << a[i] << " ";    }    cout << endl;}void TestQuickSort(){    int a[] = { 2, 0, 4, 9, 3, 6, 8, 5, 7, 1, 5 };    size_t length = sizeof(a) / sizeof(a[0]);    QuickSortR(a, 0, (length - 1));    PrintArray(a, length);}

3.前后指针法:
a.算法思想:选取最后一个值为基准值key,定义两个指针一个是cur指向开始位置,prev指向cur的前一个
位置,遍历数组,如果a[cur]<=key,那么++prev,不交换cur和prev的值,++cur,如果a[cur]>key,那么
++cur,直到a[cur]<=key,++prev此时prev!=cur,交换cur和prev指向的值,直到cur==right,然后
++prev,交换cur和prev指向的值,此时一轮排序结束。
这里写图片描述

代码实现:

//前后指针法int PartSort(int* a, int left, int right){    int cur = left;    int prev = cur - 1;    while (cur < right)    {        if (a[cur] <= a[right])        {            ++prev;            if (a[prev] != a[cur])                swap(a[prev], a[cur]);        }        ++cur;    }    ++prev;    swap(a[prev], a[cur]);    return prev;}void QuickSortR(int *a, int left,int right){    int mid;    if(left < right)    {        mid = PartSort(a, left, right);        QuickSortR(a, left, mid - 1);        QuickSortR(a, mid + 1, right);    }}void PrintArray(int *a, size_t N){    for (size_t i = 0; i < N; i++)    {        cout << a[i] << " ";    }    cout << endl;}void TestQuickSort(){    int a[] = { 2, 0, 4, 9, 3, 6, 8, 7, 5, 1, 5 };    size_t length = sizeof(a) / sizeof(a[0]);    QuickSortR(a, 0, (length - 1));    PrintArray(a, length);}

快排优化算法:
1.三数取中法
算法思想:当我们每次取得基准值都是最大的或最小的,那么快排就会变得很慢,类似于冒泡排序,
时间复杂度为o(n^2),所以我们每次期待选到比较居中的数,采取三数取中法,在第一个数
还有最后一个数以及中间的那个数,挑选中间的数。

int GetBestKey(int *a, int left, int right)  //三数取中法{    int mid = left + (right - left) >> 1;    if (a[left] < a[right])    {        if (a[mid]>a[right])            return right;        else if (a[mid] < a[left])            return left;        else            return mid;    }    else //left>right    {        if (a[mid] > a[left])            return left;        else if (a[mid] < a[right])            return right;        else            return mid;    }}

2.小区间优化法:
算法思想:当我们对海量数据进行排序时,递归是值得的,如果区间被划分为很小的时候,不用递归
直接采用直接插入排序,因为区间很小,由快排的特性可知,小区间已经快接近有序,当接近有序采用
直接插入排序的时间复杂度为o(n);节省了快排压栈的开销(对快排的几种方法都适用)。

完整代码:

int GetBestKey(int *a, int left, int right)  //三数取中法{    int mid = left + (right - left) >> 1;    if (a[left] < a[right])    {        if (a[mid]>a[right])            return right;        else if (a[mid] < a[left])            return left;        else            return mid;    }    else //left>right    {        if (a[mid] > a[left])            return left;        else if (a[mid] < a[right])            return right;        else            return mid;    }}void InsertSort(int* a, size_t N){    for (size_t i = 0; i < N - 1; i++)    {        int end = i;        int tmp = a[end + 1];        for (; end >= 0; --end)        {            if (a[end]>tmp)            {                a[end + 1] = a[end];            }            else            {                break;            }        }        a[end + 1] = tmp;    }}//左右指针法int PartSort(int *a, int left, int right){    int mid = GetBestKey(a, left, right);    swap(a[mid], a[right]);    int key = right; //最后一个下标为基准值    while (left < right)    {        while (left < right&&a[left] <= a[key])        {            ++left;        }        while (left < right&&a[right] >= a[key])        {            --right;        }        if (left<right)            swap(a[left], a[right]);    }    swap(a[left], a[key]);    return left;}void QuickSortR(int *a, int left, int right){    int mid;    if (left < right)    {        if ((right - left) < 5)        {            InsertSort(a + left, right - left + 1);        }        mid = PartSort(a, left, right);        QuickSortR(a, left, mid - 1);        QuickSortR(a, mid + 1, right);    }}void PrintArray(int *a, size_t N){    for (size_t i = 0; i < N; i++)    {        cout << a[i] << " ";    }    cout << endl;}void TestQuickSort(){    int a[] = { 2, 0, 4, 9, 3, 6, 8, 7, 1, 5 };    size_t length = sizeof(a) / sizeof(a[0]);    QuickSortR(a, 0, (length - 1));    PrintArray(a, length);}

在对海量数据进行排序时,有可能递归栈溢出,我们可以采用非递归的方式,借用栈。

void QuickSortNR(int *a, int begin, int end){    assert(a);    stack<int> s;    if (begin < end)    {        s.push(end);        s.push(begin);        while (!s.empty())        {            int left = s.top();            s.pop();            int right = s.top();            s.pop();            int mid = PartSort(a, left, right);            if (left < mid - 1)            {                s.push(mid - 1);                s.push(left);            }            if (mid + 1 < right)            {                s.push(right);                s.push(mid + 1);            }        }    }}