快速排序

快速排序

  快速排序(Quicksort)是对冒泡排序的一种改进。它由C。 A。 R。 Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

原理

  首先我们观察这样的一个数组 {31425978} ,这是一个乱序数组,我们观察其中的元素5,发现虽然数组乱序,但是5前面的所有数都比小于等于它,而它后面的所有数都大于等于它,在不考虑数组排序后稳定性的情况下,很容易就知道,在数组排好序的情况下,元素5还是处于这个位置。
  快速排序就是基于以上理论的一个排序方法。对于一个数组,我们先取数组中的一个元素做为基准值,然后把比它小元素的都移到它前面,比它大的元素都移到它后面,这样我们就确定了这个元素在排序之后正确的位置。接下来,对这个元素左边和右边的子数组,进行相同的操作,直到所有的元素都被放在正确的位置上。(递归)

分析

设有长度为 N 的数组 A。
1。 设置两个变量i、j,排序开始的时候分别指向数组最左和最右,即 left = i = 0,right = j = N-1
2。 以最右的数组元素作为关键数据,赋值给key,即key = A[right]
3。 从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于等于key的A[i],将A[i]的值赋给a[j]
4。 从j开始向前搜索,即由后开始向前搜索(j–),找到第一个小于等于key的值A[j],将A[j]的值赋给A[i]
5。 重复第3、4步,直到i=j,此时令循环结束,将key的值赋给a[i]
6。从索引i将数组分割成索引left到i-1及索引i+1到right的两个子数组,重复上面1到5步,直到最后left = right。

图解

下图解释了快速排序的一次排序过程

这里写图片描述

代码

void quickSort(int* arr, int left, int right)
{
    if(left < right) {
        int i = left, j = right;
        //取最后一位为基准值
        int pivot = arr[right];
        while(i < j) {
            while(i < j && arr[i] < pivot) {
                i++;
            }
            if(i < j) {
                arr[j--] = arr[i];
            }
            while(i < j && arr[j] > pivot) {
                j--;
            }
            if(i < j) {
                arr[i++] = arr[j];
            }
        }
        arr[i] = pivot;
        quickSort(arr, left, i - 1);
        quickSort(arr, i + 1, right);
    }
}

其它方法

上面的方法,每次要先操作左边元素,再操作右边元素,我们可以用交换法,来同时操作一个不大于基准的元素和一个不小于基准的元素,每当i指向一个不小于基准的元素并且j指向一个不大于基准的元素,我们就把a[i]与a[j]交换,这样可以更加高效,代码如下。

void swap(int* a, int* b)
{
    if(a == b)
        return;
    int temp = *a;
    *a = *b;
    *b = temp;
}
void quickSort(int* arr, int left, int right)
{
    if(left < right) {
        int i = left, j = right - 1;
        //取最后一位为基准值
        int pivot = arr[right];
        while(i <= j) {
            while(i <= j && arr[i] < pivot) {
                i++;
            }

            while(i <= j && arr[j] > pivot) {
                j--;
            }
            if(i <= j) {
                swap(arr + i, arr + j);
                i++;
                j--;
            }
        }
        swap(arr + i, arr + right);
        quickSort(arr, left, i - 1);
        quickSort(arr, i + 1, right);
    }
}

还有很多种其它的快速排序方法,这里就不一一赘述了。比如以上两种方法都是ij从左右两边分别向内移动的的,还有ij都从左或者从右向另一个方向移动的方法。还有关于基准值的选择,上面两个代码也是用了最不可取的方法,取了最左或最右的值,一个比较好的方法是取最左,最右和中间数三者之间的中值。

PS

  上面两个代码中,i和j停下来的条件都是指向了不小于或者不大于基准的值,相等也是包含在里面的,这样做一个好处是防止了递归只发生在数组的一边。

  另外,之前看到有人在群里求助,说面试的时候碰到一首题给定一个数组,让选第一次快速排序之后数组的顺序…我就觉得这样的题特别没意义,快排根本没有固定的写法,你用不同的方法,第一次排序之后得到的数组顺序肯定不会是相同的,即使你选的是相同的基准值。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值