【第21期】观点:人工智能到底用 GPU?还是用 FPGA?

排序之快速排序

原创 2016年06月01日 18:59:46

本文以《啊哈!算法》为教材,个人理解整理。

例:将10个数字进行从小到大排序输出。
  输入:6 1 2 7 9 3 4 5 10 8
  输出:1 2 3 4 5 6 7 8 9 10

快速排序首先将最左边的第一个数作为基准数,即6,然后设置两个标志 i 和 j , i 用来从左向右寻找比6大的数, j 用来从右向左寻找比6小的数,当各自找到时,循环暂停,交换这两个数字位置,之后继续循环寻找,直到 i 和 j 在同一个位置碰头,结束本轮循环,将当前 i 和 j 同处位置上的数字与基准数6交换。之后借助递归用相似的方式排列好基准数的左边数列和右边数列。

如例中第一轮循环:
这里写图片描述

首先从右边开始移动 j (为什么从右边开始最后再讨论),寻找到一个比6小的数字,可知是5,则 j 停留在5的位置。之后从左边开始移动 i ,寻找到一个比6大的数字,可知是7,则 i 停留在7的位置。如下:
这里写图片描述

之后交换7和5的位置,数列变为:
这里写图片描述

然后重新开始寻找,从右边开始, j 将停留在4, i 将停留在9,并且交换它们:
这里写图片描述

最后, j 先向左寻找,停留在3,而因为 i 必须小于或者等于 j,因此 i 也只能停留在3,至此 i 和 j 碰头,第一轮循环结束,交换3和基准数6,数列变为:
这里写图片描述

之后采用递归分别对6左边的数列 3 1 2 5 4和6右边的数列9 7 10 8进行同样的操作,最终完成排序,这里引用《啊哈!算法》中的图更直观:

以下是代码,在写quickSort时,一开始我想为什么不直接使用left和right,而是又将它们分别赋值给 i 和 j 之后才使用。所以我忽略了 i 和 j ,直接用left和right,但是很显然我错了,因为写到代码28行时,也就是要将i、j 碰头处和基准数6交换时,我发现我找不到6的位置了。。。才恍然大悟 i 和 j 的用处。

原本书中的代码可以更简单易懂,之所以使用了malloc动态定义数组是因为大学学C时对这个一直懵懵懂懂,借这个机会再巩固巩固。

#include<stdio.h>
#include<stdlib.h>
int *a;

void quickSort(int left, int right)
{
    int i, j, t, p;
    if(left > right)
        return;

    p = a[left]; //基准数 
    i = left;
    j = right; //赋值给i和j保证left和right位置不动 
    while(i != j)
    {
        while(i < j && a[j] >= p) //要从右边开始寻找 
            j--;

        while(i < j && a[i] <= p)
            i++;

        if(i < j) //当i和j没有碰头时 
        {
            t = a[i]; a[i] = a[j]; a[j] = t;
        }
    }

    a[left] = a[i]; a[i] = p; //i和j碰头,交换碰头处数字和基准数 

    quickSort(left, i-1); //递归基准数左边 
    quickSort(i+1, right); //递归基准数右边 
    return;
}

int main()
{
    int n, i;
    scanf("%d", &n);
    a = (int *)malloc(sizeof(int)*n); //为数组a动态分配长度 
    for(i = 0; i < n; i++)
    {
        scanf("%d", &a[i]);
    }

    quickSort(0, n-1);

    for(i = 0; i < n; i++)
    {
        printf("%d ", a[i]);
    }
    return 0;
}

至于为什么要从右边开始移动,百度了一下还没有发现真正解释清楚的内容,不过在知道里找到一个反例,可以帮助理解。

例:3 1 2 5 4

这里基准数为3,如果从左边开始移动则 i 停留在5,而 j 也就不得不停留在5,这时 i 和 j 在5处碰了头,也就意味着要将3和5进行交换,那么数列将变为:5 1 2 3 4

很明显,这样再接下去无论如何结果都是错误的。但是如果按照快速排序从右边开始移动,则 j 停留在2,i 也停留在2, i 和 j 在2处碰了头,交换2和3,数列变为:2 1 3 5 4。显然这样才正确。

为什么会造成这样的情况?一开始我猜测是不是因为现在操作的是从小到大,假如换成从大到小,是不是就应该从左边开始了。于是用3 1 2 5 4实践了下,经过实践发现从大到小也是需要从右边开始的。

然后我把基准数换成了最右边的数,即4,测试下了下发现原来是这个影响了顺序,同样使用3 1 2 5 4,基准数改为4后,从右边就行不通了,而从左边才能够继续下去。至于为什么是这样,我也没想明白,总觉得这会是一个数学题。

版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

快速排序

快速排序 通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据...

快速排序

  高快省的排序算法 <p style="margin

快速排序算法 c语言

快速排序算法实现

啊哈算法-----快速排序

上一节的冒泡排序可以说是我们学习第一个真正的排序算法,并且解决了桶排序浪费空间的问题,

快速排序 java实现

解题思路: 快速排序的基本思想是:先对数组进行一次划分partition。划分的目的是在数组中选择一个数,作为支点pivot,数组中所有比pivot小的元素放置在数组的左边,数组中所有比pivot大...
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)