排序之快速排序

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

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

数据结构实验之排序八:快速排序 递归

Time Limit: 1000MS Memory Limit: 65536KB Problem Description 给定N(N≤10^5)个整数,要求用快速排序对数据进行升序排列,注意不得...
  • khn64
  • khn64
  • 2017年02月08日 19:22
  • 193

数据结构实验之排序八:快速排序 (sdut oj)

数据结构实验之排序八:快速排序 Time Limit: 1000MS Memory Limit: 65536KB Problem Description 给定...
  • SwordsMan98
  • SwordsMan98
  • 2017年04月21日 16:46
  • 197

lintcode 整数排序 1和2

package cn.edu.tju.cs.bigdata.chc; public class 整数排序1 { /** * @author 小锦囊 * 冒泡排序 */ publi...
  • u014470581
  • u014470581
  • 2016年10月16日 15:53
  • 622

【数据结构与算法】内部排序之四:归并排序和快速排序(含完整源码)

之所以把归并排序和快速排序放在一起探讨,很明显两者有一些相似之处:这两种排序算法都采用了分治的思想。下面来逐个分析其实现思想。 归并排序 实现思想 归并的含义很明显就是将两...
  • mmc_maodun
  • mmc_maodun
  • 2014年03月06日 00:02
  • 7419

数据结构实验之排序一:一趟快排

数据结构实验之排序一:一趟快排 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 给定...
  • guoqingshuang
  • guoqingshuang
  • 2015年12月20日 19:01
  • 2387

经典算法与数据结构的c++实现——快速排序

因为是经典的算法,所以网上描述一大把,直接上个图,直观些,给记性不好的菜鸟(如我)一点儿提示。 快速排序是冒泡排序的一种改进。通过轴值的选择方法不同,算法的速度也不一样。本文下面的代码采取选择最左...
  • y277an
  • y277an
  • 2016年03月22日 17:24
  • 636

分治法之快速排序算法理解介绍

分治法之快速排序算法理解介绍
  • qq_26071477
  • qq_26071477
  • 2016年06月11日 21:35
  • 1595

快速排序的vector简易示例

#include using namespace std; #include using std::vector; void quicksort(vector& vint,int s,int ...
  • goodcat12
  • goodcat12
  • 2012年06月04日 11:42
  • 1976

冒泡排序和快速排序以及Android的collections.sort排序

1.冒泡排序 (1)基本思想:在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序...
  • u014544193
  • u014544193
  • 2016年04月15日 16:50
  • 3614

八大排序算法 之 快速排序(填坑法)

排序思想: 1,将第一个数字作为基准数字,将数组分为左右两部分,左边是比它小的数字,右边是比它大的数字; 2,将左部分按照上面的思想再次进行划分成两部分,依次类推; 3,将右部分按照上面的思想再...
  • yxb_yingu
  • yxb_yingu
  • 2016年05月07日 11:36
  • 1036
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:排序之快速排序
举报原因:
原因补充:

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