数据结构及算法——快速排序

一、关于快速排序的思想
快速排序是一种分治的思想,它通过一趟排序将待排序记录分割成独立的两个部分,其中的一部分关键字均比另一部分的关键字小,再分别对这两部分记录继续进行排序,以便达到整个序列有序的目的。

二、快速排序的代码(来源于大话数据结构)

#include <iostream>
#include <initializer_list>
#define MAXSIZE 10000
#define TRUE 1
#define FALSE 0
#define MAX_LENGTH_INSERT_SORT 7
typedef int Status;
struct SqList
{
    int r[MAXSIZE+1]= {0, 50, 10, 90, 30, 70, 40, 80, 60, 20};
    int length = 9;
};
void swap(SqList &L, int i, int j)
{
    int temp = L.r[i];
    L.r[i] = L.r[j];
    L.r[j] = temp;
}
//插入排序,用于在数据量小于某个值时的备选排序方法
void InsertSort(SqList &L)
{
    int i, j;
    for(i=2; i<=L.length; ++i)
    {
        if(L.r[i]<L.r[i-1])
        {
            L.r[0] = L.r[i];
            L.r[i] = L.r[i-1];
            for(j=i-2; L.r[j]>L.r[0]; --j)
                L.r[j+1] = L.r[j];    
            L.r[j+1] = L.r[0];
        }
    }
}
int Partition(SqList &L, int low, int high);
void QSort(SqList &L, int low, int high);
void QuickSort(SqList &L)
{
    QSort(L, 1, L.length);
}
void QSort(SqList &L, int low, int high)
{
    int pivot;
    if(L.length>MAX_LENGTH_INSERT_SORT)
    {
        while(low<high)
        {
            pivot = Partition(SqList &L, int low, int high);
            QSort(L, low, pivot-1); //对低子表递归排序
            //QSort(L, pivot+1, high);
            low = pivot+1; //尾递归,用于缩减递归堆栈深度
        }
    }
    else
    {
        InsertSort(L);
    }
}
int Partition(SqList &L, int low, int high)
{
    int pivotkey;
    //取左端、中间以及右端三个数中的中间值作为枢轴,避免出现极端分布的情况
    int m = (low+high)/2;
    if(L.r[low]>L.r[high])
        swap(L, low, high);
    if(L.r[m]>L.r[high]);
        swap(L, m, high);
    if(L.r[low]<L.r[m])
        swap(L, low, m);
    
    pivotkey = L.r[low];
    L.r[0] = pivotkey;
    while(low<high)
    {
       while(low<high && L.r[high]>=pivotkey)
           --high;
        L.r[low] = L.r[high];
        while(low<high && L.r[low]<=pivotkey)
            ++low;
        L.r[high] = L.r[low];
    }
    L.r[low] = L.r[0];
    return low;
}
int main()
{
    SqList L;
    QuickSort(L);
    for(int i=1; i<=L.length; ++i)
        std::cout << L.r[i] << " ";
}

三、关于优化后的代码
1、优化了Partition函数中枢轴的选取,采用三数取中的方法,避免因为开始选取的pivotkey值过大或者过小,而导致分割成的两部分元素个数相差过大,最后得到的递归树深度过深,从而使得时间复杂度和空间复杂度不够理想;
2、增加了直接插入排序作为备选,快速排序适合用于数据量较大的情况,而直接插入排序适合用于数据量较小的情况,故增加备选,以保证不同数据量的处理也能得到较好的性能;
3、优化了递归操作,将原本的QSort函数尾部的两次递归操作改变为尾递归优化,采用迭代而非递归,以便缩减堆栈深度,从而提高性能。(此处本人还存在疑惑,需要进一步学习)

四、关于时间复杂度和空间复杂度
在最优的情况下,Partition每次划分得很均匀,假设排序的关键字为n个,则此时的时间复杂度为O(nlogn);在最坏的情况下,待排序的序列为正序或者逆序,每次划分只得到比上一次划分少一个记录的子序列,另一个子序列为空,此时需要执行n-1次递归调用,且第i次划分需要经过n-i次关键字的比较才能找到第i个记录,即枢轴的位置,故总的比较次数为n-1+n-2+…+1=n(n-1)/2,其最终的时间复杂度为O(n^2)。
经过数学归纳法等证明,平均情况下,时间复杂度为O(nlogn)。
关于空间复杂度,主要是由于递归造成的栈空间的使用,最好的情况下,递归树的深度为log2 n,即空间复杂度为O(logn),最坏的情况下,需要进行n-1次递归调用,空间复杂度为O(n),平均情况下,空间复杂度为O(logn)。

五、稳定性
由于关键字的比较和交换为跳跃式的,故快速排序是一种不稳定的排序方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值