排序算法:快速排序

排序思想:

快速排序是对冒泡排序的一种改进。其基本思想是基于分治法的:在待排序表[1…n]中任取一个元素pivot作为基准,通过一趟排序将待排序表划分为独立的两部分L[1…k-1]和L[k+1…n],使得L[1...k-1]中所有元素小于等于pivot,L[k+1…n]中所有元素大于等于pivot,则pivot放在了其最终位置L(k)上,这个过程称为一趟快速排序。而后分别递归地对两个子表重复上述过程,直至每部分内只有一个元素或空为止,即所有元素放在了其最终位置上。

首先假设划分算法已知,记为Partition(),返回的是上述中的k,注意到L(k)已经在最终的位置,所以可以先对表进行划分,而后对两个表调用同样的排序操作。因此可以递归地调用快速排序算法进行排序,具体的程序结构如下:

void QuickSort(ElemType A[], int low, int high)
{
    if(low < high) // 边界条件,即递归跳出的条件
    {
        // Partition()就是划分操作,将表A[low...high]划分为满足上述条件的两个子表
        int pivotpos = Partition(A, low, high); // 划分
        QuickSort(A, low, pivotpos - 1); //依次对两个子表进行递归排序
        QuickSort(A, pivotpos + 1, high);
        }
}

从上面的代码也不难看出快速排序算法的关键在于划分操作,同时快速排序算法的性能也主要取决于划分操作的好坏。

 

一趟快速排序的算法过程是:

1)设置两个索引low、high,排序开始的时候:low=0,high=n-1;

2)以第一个数组元素作为枢轴值,赋值给pivot,即pivot = A[low];

3)从high开始向前搜索,即由后开始向前搜索(high--),找到第一个小于pivot的值A[high],将A[high]的值赋给A[low] ( A[low] = A[high] )

4)从low开始向后搜索,即由前开始向后搜索(low++),找到第一个大于pivot的A[low],将A[low] 的值赋给 A[high] (A[high] = A[low] )

5)重复第3、4步,直到 low=high;(3,4步中,没找到符合条件的值,即3中A[high]大于等于pivot,4中A[low]小于等于pivot的时候改变high、low  的值,使得hihg = high-1,low = low+1,直至找到为止。找到符合条件的值,进行交换的时候low,high 指针位置不变。另外,low==high  这一过程一定正好是low++或high--完成的时候,此时令循环结束)。

将上述过程转化为代码:

int Partition(ElemType A[], int low, int high)
{
    ElemType pivot = A[low];    // 将当前表中第一个元素设为枢轴值,对表进行划分
    while(low < high)           // 循环跳出条件
    {
        while(low < high && A[high] >= pivot)
            --high;
            A[low] = A[high];   // 将比枢轴值小的元素移动到左端
        while(low < high && A[low] <= pivot)
            ++low;
            A[high] = A[low];   // 将比枢轴大的元素移动到右端
    }
    A[low] = pivot;             // 枢轴元素存放到最终位置
    return low;                 // 返回存放枢轴的最终位置
}

排序演示:

设待排序表A[8] = {4,6,2,3,1,5,7,9};

1)以第一个数组元素作为枢轴值,赋值给pivot,即 pivot = A[0] = 4;

 2)从high开始向前搜索,即由后开始向前搜索(high--),循环程序,找到第一个小于pivot的值 A[4] = 1,将A[4] = 1 赋给 A[0] (A[0] = A[4])

3) 从low开始向后搜索,即由前开始向后搜索(low++),找到第一个大于pivot的A[1] = 6,将A[2] = 6 赋给 A[4] (A[4] = A[2]);

4) 继续从high开始向前搜索,找到第二个小于pivot的值 A[3] = 3,赋值给A[low], A[1] = A[3] = 3

5)然后再从low开始向后搜索,直到low = high ,没有找到大于pivot 的元素,循环停止;此时low = high = 3;

 6) 执行A[low] = pivot,将枢轴元素存放到最终位置

7)此时,一趟快速排序执行完毕:枢轴值左边的表值都不大于4,右边的表值都不小于4;

8)再分别对两个子表进行递归快速排序,知道表中所有元素都排好序。

算法复杂度分析:

空间复杂度

递归造成的栈空间的使用,最好情况,递归树的深度为log2n,其空间复杂度也就为O(logn),最坏情况,需要进行n-1递归调用,其空间复杂度为O(n),平均情况,空间复杂度也为O(logn)。

时间复杂度:

在最优情况下,Partition每次都划分得很均匀,如果排序n个关键字,其递归树的深度就为(log2n)+1(以2为底;向下取整),即仅需递归log2n次(以2为底),需要时间为T(n)的话,第一次Partition应该是需要对整个数组扫描一遍,做n次比较。然后,获得的枢轴将数组一分为二,那么各自还需要T(n/2)的时间,也就是说,在最优的情况下,快速排序算法的时间复杂度为O(logn)。

在最坏的情况下,待排序的序列为正序或者逆序,每次划分只得到一个比上一次划分少一个记录的子序列,注意另一个为空。如果递归树画出来,它就是一棵斜树。最终其时间复杂度为O(n^2)。

平均情况下,复杂度为O(nlogn)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值