排序算法之快速排序

快速排序是一种高效的排序算法,平均时间复杂度为Θ(nlogn),最坏情况下为Θ(n2)。它采用分治策略,通过选取基准元素将数组分为两部分,然后对两部分递归进行排序。该算法能进行原址排序,无需额外存储空间。本文将详细解释快速排序的原理,并提供代码示例。

快速排序算法是对冒泡排序算法的改进,它通常是实际排序应用中最好的一个选择。它的平均性能较好,虽然最坏情况下的时间复杂度为 Θ(n2) 比较高,但它的期望时间复杂度为 Θ(nlogn) 。还有它能够进行原址排序,原址排序是指:在排序算法中对数组中元素进行重排,任何时候数组中仅有常数个元素需要存储在数组之外。插入排序和堆排序也属于原址排序。
快速排序算法的基本思想是:选取一个基准元素,通过一次排序将带排序的记录分成独立的两部分,使得左边部分记录的元素均小于或等于基准元素,右边部分记录的元素均大于或等于基准元素,再分别对这两部分记录进行快速排序,最终达到整个序列有序。
可以看出快速排序算法使用的是分治思想,下面对子数组A[low..high] ( A[low..high]表示数组A的一个子数组,包含元素A[low],A[low+1],…,A[high-1],A[high] )的快速排序的分治过程进行描述如下:
(1)选取一个基准元素设为key,将A[low..high]划分为A[low..point-1]和A[point+1..high],使得A[low..point-1]中的每一个元素均小于等于key,A[point+1..high]中的每一个元素均大于等于key,在此划分过程中同时要计算出下标point即划分过后基准元素key所在位置,再将key存放在元素A[point]上。
(2)通过递归调用快速排序,对划分的子数组A[low..point-1]和A[point+1..high]进行排序。
因为子数组都是进行原址排序,所以不需要进行合并,最后便可直接得到有序的数组A[low..high]。
对文字描述还没有什么感觉的话,可以接着看代码。

// 对子数组A[low..high]进行快速排序
void QuickSort(int A[], int low, int high)
{
    int point;

    if( low < high )                        // 至少要有两个元素
    {
        point = Partition(A, low, high);    // 划分并得到下标point

        QuickSort(A, low, point-1);         // 对左边部分进行快速排序

        QuickSort(A, point+1, high);        // 对右边部分进行快速排序
    }
}

算法的关键部分是划分过程,即上面代码中的Partition函数过程,它实现了对子数组A[low..high]的原址重排。

// Partition函数实现
int Partition(int A[], int i, int j)
{
    int key = A[i];           // 选取子数组的第一个元素为基准元素

    while (i < j)
    {
        while (i < j && A[j] >= key)     // 找到比基准元素值小的元素
        {
            --j;
        }
        if (i < j)      // 将比基准元素值小的元素交换到低端
        {
            A[i++] = A[j];
        }

        while (i < j && A[i] <= key)      // 找到比基准元素值大的元素
        {
            ++i;
        }
        if (i < j)      // 将比基准元素值大的元素交换到高端
        {
            A[j--] = A[i];
        }
    }
    A[i] = key;

    return i;         // 返回基准元素所在位置
}

为了更好的理解代码,这里再用示例加以说明。下图纯粹手画,大家凑合着看啊。
假设对数组A[10]={80,16,100,35,85,20,12,90,110,5}进行快速排序,划分过程即执行Partition(A, 0, 9)的过程如下所示:

划分过程

具体的完整代码如下:

#include <stdio.h>

void swap(int A[], int low, int high)
{
    int temp;

    temp = A[low];
    A[low] = A[high];
    A[high] = temp;
}

// Partition函数实现
int Partition(int A[], int i, int j)
{
    int key = A[i];           // 选取子数组的第一个元素为基准元素

    while (i < j)
    {
        while (i < j && A[j] >= key)     // 找到比基准元素值小的元素
        {
            --j;
        }
        if (i < j)      // 将比基准元素值小的元素交换到低端
        {
            A[i++] = A[j];
        }

        while (i < j && A[i] <= key)      // 找到比基准元素值大的元素
        {
            ++i;
        }
        if (i < j)      // 将比基准元素值大的元素交换到高端
        {
            A[j--] = A[i];
        }
    }
    A[i] = key;

    return i;         // 返回基准元素所在位置
}

// 对子数组A[low..high]进行快速排序
void QuickSort(int A[], int low, int high)
{
    int point;

    if( low < high )                        // 至少要有两个元素
    {
        point = Partition(A, low, high);    // 划分并得到下标point

        QuickSort(A, low, point-1);         // 对左边部分进行快速排序

        QuickSort(A, point+1, high);        // 对右边部分进行快速排序
    }
}

int main()
{
    int a[] = { 80,16,100,35,85,20,12,90,110,5 };
    int len = sizeof(a) / sizeof(int);

    QuickSort(a, 0, len - 1);

    printf("排序后的结果是:");
    for (int i = 0; i < len; i++)
    {
        printf("%d ", a[i]);
    }
    printf("\n");

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值