排序之快速排序

原创 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后,从右边就行不通了,而从左边才能够继续下去。至于为什么是这样,我也没想明白,总觉得这会是一个数学题。

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

快速排序代码

  • 2016年06月14日 14:08
  • 2KB
  • 下载

简单的快速排序

  • 2015年01月23日 09:57
  • 479B
  • 下载

java实现冒泡、选择、快速排序算法

冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列...

快速排序算法

  • 2015年05月01日 16:41
  • 1KB
  • 下载

C#快速排序练习

  • 2017年06月20日 11:40
  • 30KB
  • 下载

快速排序(quicksort)

IT公司招人,无论笔试还是面试,快排基本上都会问到。通常情况下,快排实现有两个函数: void quickSort(int arr[], int left, int right); int parti...

快速排序原创数据(20组)

  • 2015年10月30日 21:48
  • 7.57MB
  • 下载

POJ1328 -- 贪心算法和快速排序

一 。贪心策略:将雷达放置在合适的地方,使得包含的岛屿数量最多。 二。具体分析 我们采用从左到右放置雷达。假设左边第一个岛屿是A(Xa,Ya),从左到右的岛屿依次是A B C .... 我们可以计算出...

冒泡归并和快速排序java源码

  • 2016年05月11日 20:30
  • 2KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:排序之快速排序
举报原因:
原因补充:

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