排序算法之快速排序

快速排序是一种不稳定的排序算法,它的基本思想是,以某个元素为基准,将所有大于等于它的值放在右边,小于它的值放在左边,这样数组就被分为两部分,递归对这两部分进行快速排序,而单个元素我们认为是已经排好序的。这是一种归并思想,当然在最后一步,合并,我们什么也没有做也不用做。每一次排序都有一个元素被放在正确的位置,所以该算法总是会结束。因此,算法的精髓在于如何选择基准并分为两个子数组。选择基准有很多种方式,有直接选择首元素和末尾元素的,可是如果数组本身已经有序,这样的选择不是那么明智的,因为这样行形成的子数组一个元素个数为0,另一个为n-1,当然如果无序数列的随机性比较好,这样选择也是无可厚非的。比较好的提议有选择中间位置的元素作为基准,或者选择使用随机数随机的位置的元素作为基准。下面的partion版本则是以无序区最右边的数字作为基准,很囧,选择了一个不怎么明智的基准。

int Partion(int a[], int n, int left, int right)
{
    int index = right; //index用来记录大于等于基准元素的界限,也就是index右边的元素均大于基准元素
    int pivot = a[index]; //最右侧元素为基准

    for (int i = right - 1; i >= left; i--) //从右侧倒数第二个元素开始遍历,直到最左端
    {
        if (a[i] > pivot) {      //大于哨兵,交换
            myexchange(a, i, --index);
        }
    }
    myexchange(a, right, index);  //基准元素位于正确的位置,即index

    return index;
}

然后可以开始调用此函数,进行递归的快速排序。

void EX_SortQuick(int a[], int n, int left, int right)
{
    if (right > n || (right - left) >n || right < left || left < 0 || right < 0) {
        return;
    }

    if (left >= right) {
        return;
    }

    int mid = Partion(a, n, left, right);
    EX_SortQuick(a, n, left, mid - 1);
    EX_SortQuick(a, n, mid + 1, right);
    return;
}

快速排序函数本身很简单,所以可以尝试改进partion函数的实现,上面的版本我们从数组的一头开始遍历,以index为大于基准元素的界限。进一步想,partion最终结果是数组分为了两个子数组,总之呢,第一个元素肯定是小于基准,最后一个元素肯定是大于等于基准,我们准备两个指针,一个指向序列头,一个指向序列尾部,同时遍历,若指向头的指针小于基准,它已经在正确的子数组了,当它遇到大于基准的值时,停了下来,然后开始遍历尾部的指针,相反地,当它的值大于基准时,它在正确的子数组,否则就停下来。这样,当两个指针停下来的时候,前面的指针指向大于基准的值,后面的指针指向小于基准的值,于是交换两个指针指向的元素,他们就位于正确的子数组中了,然后递增(递减)前后指针,开始下一次比较。一直到两个指针相遇,整个数组就被分成了两个部分。比如序列a[] = {3,2,1,6,23,4,7,90,45,9};以9为基准,当i遍历到23(i = 4),j遍历到7的时候(j = 6),交换7和23,然后i递增指向23,此时i = 6,指针相遇,循环退出,此时i左边的数字都比9小,交换i位置的数和9,于是整个数组就变为{3,2,1,6,7,4, 9 ,90,45,23},返回i的值,9排到了正确的位置。

版本二:两个指针遍历的partion函数

int Partion(int a[], int n, int left, int right)
{
    int pivot = a[right];
    int i = left, j = right - 1;
    while (i < j) {
        while (i < j && a[i] < pivot) {
            i++;
        }

        while (i < j && a[j] > pivot) {
            j--;
        }

        if (i != j) {
            myexchange(a, i, j);
        }
    }
    if (a[i] > pivot) { //该处判断的意义在于,若子数组为两个元素,便不会进入循环,一个元素为基准和另一个比较
        myexchange(a, i, right);
    }

    return i;
}
其实当排序范围较小的时候,应选择插入或者选择排序,因此当子数组小于一定数目的时候,可以适当选择恰当的排序方式,该问题以后讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值