iOS - 快速排序

Demo_github

图片源于网络

快速排序

快速排序(Quick Sort)是对冒泡排序的一种改进。通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

算法思想

  • 从数列中挑出一个元素,称为 "基准"(pivot)

  • 分割(partition)操作:重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分割之后,该基准是它的最后位置。

  • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

  • 快速排序是基于分治模式处理的,对一个典型子数组A[p...r]排序的分治过程为三个步骤:

    • 分解:
      A[p..r]被划分为俩个(可能空)的子数组A[p ..q-1]和A[q+1 ..r],使得
       A[p ..q-1] <= A[q] <= A[q+1 ..r]

    • 解决:
      通过递归调用快速排序,对子数组A[p ..q-1]和A[q+1 ..r]排序。

    • 合并:
      将排序好的子数组A[p ..q-1]和A[q+1 ..r]合并

图-快速排序示例图

上图中,演示了快速排序的处理过程:

  • 初始状态为一组无序的数组:{2,4,5,1,3}

  • 经过以上操作步骤后,完成了第一次的排序,得到新的数组:{1,2,5,4,3}

  • 新的数组中,以2为分割点,左边都是比2小的数,右边都是比2大的数。

  • 因为2已经在数组中找到了合适的位置,所以不用再动。

  • 2左边的数组只有一个元素1,所以显然不用再排序,位置也被确定。(注:这种情况时,left指针和right指针显然是重合的。因此在代码中,我们可以通过设置判定条件left必须小于right,如果不满足,则不用排序了)。

  • 2右边的数组{5,4,3},设置left指向5,right指向3,开始继续重复图中的一、二、三、四步骤,对新的数组进行排序。

范例代码

#pragma mark --- 快速排序
/**
 快速排序
 
 @param array 需要排序的Array
 @param left 初始索引
 @param right 最后一位索引
 */
+ (void)quickSort:(NSMutableArray *)array left:(int)left right:(int)right
{
    
    if(array == nil || array.count == 0 || left >= right){
        NSLog(@"注意快速排序条件");
        return;
    }
    
    // 以最左边的数(left)为基准
    int base = left;
    NSNumber *prmt = array[base];
    //    //取中值
    //    int middle = left + (right - left)/2;
    //    NSNumber *prmt = array[middle];
    int i = left;
    int j = right;
    
    /**
     while是循环流程控制,使用的标准格式为
     while(表达式)
     {
     循环语句体;
     }
     说明:①while循环的表达式是循环进行的条件,用作循环条件的表达式中一般至少包括一个能够改变表达式的变量,这个变量称为循环变量
     ②当表达式的值为真(非零)时,执行循环体;为假(0)时,则循环结束
     ③当循环体不需要实现任何功能时,可以用空语句作为循环体
     ④对于循环变量的初始化应在while语句之前进行,可以通过适当方式给循环变量赋初值
     */
    //开始排序,使得left<prmt 同时right>prmt
    while (i < j) {
        //        while ([array[j] intValue] > [prmt intValue]) {
        //该行与下一行作用相同
        // j 从右至左移动(j--) 直到找到一个小于[prmt intValue]的数停下来
        while ([array[j] compare:prmt] == NSOrderedDescending) {
            j--;
        }
        //        while ([array[i] intValue] < [prmt intValue]) {
        //该行与下一行作用相同
        /**
         i 从左至右移动(i++) 直到找到一个大于[prmt intValue]的数停下来
         */
        while ([array[i] compare:prmt] == NSOrderedAscending) {
            i++;
        }
        
        //如果i与j未相遇 交换其数据
        if(i < j){
            [array exchangeObjectAtIndex:i withObjectAtIndex:j];
            //i 与j 各移动一位 进入下一循环
            i++;
            j--;
        }
        NSLog(@"快速排序排序中:%@",array);
    }
    
    //第一轮排序完成 将数组拆成 A[p ..q-1] <= A[q] <= A[q+1 ..r] 模式的两个数组 递归
    /**
     程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。递归有直接递归和间接递归
     •直接递归:函数在执行过程中调用本身。
     •间接递归:函数在执行过程中调用其它函数再经过这些函数调用本身。
     •递归算法有四个特性:
     (1)必须有可最终达到的终止条件,否则程序将陷入无穷循环;
     (2)子问题在规模上比原问题小,或更接近终止条件;
     (3)子问题可通过再次递归调用求解或因满足终止条件而直接求解;
     (4)子问题的解应能组合为整个问题的解。
     */
    
    // 对“基准标号“左侧的一组数值进行递归的切割,以至于将这些数值完整的排序
    if (left < j) {
        [self quickSort:array left:left right:j];
    }
    // 对“基准标号“右侧的一组数值进行递归的切割,以至于将这些数值完整的排序
    if (right > i) {
        [self quickSort:array left:i right:right];
    }
}

算法分析

  • 快速排序的算法性能
快速排序的算法性能
  • 时间复杂度
    • 快速排序涉及到递归调用。其递归算法的时间复杂度公式:T[n] = aT[n/b] + f(n)

    • 最优情况下时间复杂度

      快速排序最优的情况就是每一次取到的元素都刚好平分整个数组;

      此时的时间复杂度公式则为:T[n] = 2T[n/2] + f(n);T[n/2]为平分后的子数组的时间复杂度,f[n] 为平分这个数组时所花的时间;

      快速排序最优的情况下时间复杂度为:O( N*log N )
    • 最差情况下时间复杂度

      最差的情况就是每一次取到的元素就是数组中最小/最大的,这种情况其实就是冒泡排序了(每一次都排好一个元素的顺序)

      这种情况时间复杂度就是冒泡排序的时间复杂度:T[n] = n * (n-1) = n^2 + n;

      快速排序最差的情况下时间复杂度为:O( N^2 )
    • 平均时间复杂度

      快速排序平均时间复杂度为:O( N*log N )
  • 空间复杂度

    快速排序在每次分割的过程中,需要 1 个空间存储基准值。
    最优的情况下空间复杂度为:O(log N) ;每一次都平分数组的情况
    最差的情况下空间复杂度为:O( N ) ;退化为冒泡排序的情况

  • 算法稳定性

    在快速排序中,相等元素可能会因为分区而交换顺序,如: 当待排序元素类似[6,1,3,7,3]且基准元素为6时,经过分区,形成[1,3,3,6,7],两个3的相对位置发生了改变,所是快速排序是一种不稳定排序。

参考

排序二 快速排序

坐在马桶上看算法:快速排序

图解快速排序

排序算法之 快速排序 及其时间复杂度和空间复杂度

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    蚁前阅读 5,149评论 0 52
  • 概述:排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    每天刷两次牙阅读 3,723评论 0 15
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 将一个记录插入到已排序好...
    依依玖玥阅读 1,225评论 0 2
  • 一、 单项选择题(共71题) 对n个元素的序列进行冒泡排序时,最少的比较次数是( )。A. n ...
    貝影阅读 8,888评论 0 10
  • 在别人眼中,我应该知足:在体制内上班,单位不错,职位不低,工作年年优秀,专业拿到中级证书…… 但我内心却越来越迷茫...
    VV行进中的鲤鱼阅读 743评论 16 21