zoukankan      html  css  js  c++  java
  • 细致分析快速排序!

    快速排序是当下面试最容易考的题之一。初学算法基础的伙伴一定饱受它的折磨,因为快速排序的思想让初学者有一些发懵。
    我写下这篇文章谈一谈自己对快速排序的理解,希望能够帮助初学者理解一下快速排序算法的流程。

    快速排序的思想:
    我要探讨的思想呢,不是教科书上的,而是我自己按容易理解的方法进行了改编。
    请大家跟着我的思路:

    宏观的来看:
    1 我们拿出第一个数当作基准数,把所有比他小的数放在他左边,所有比他大的数放在右边
    2 对mid左侧所有的数当成一个新的数列 进行上述排序
    3 对mid右侧所有的数当成一个新的数列 进行上述排序

    一个大数列,一点一点被我们分的越来越小,最后找到一个数做mid排完之后,左侧就剩下一个数,右侧就剩下一个数,就排完了

    具体怎么实现找到基准数位置的呢??
    我们拿到一个数列有n个数, 我们假设面前摆了n个水桶,每个水桶里面有一个数,现在数是乱序的,我们要不移动水桶,而把数取出来排序。
    我们把n个水桶从左到右编号分别为0到n-1
    ok!!!请大家跟着我的步骤哦~不要掉队!!
    1 把0号水桶的数拿在手里, 这个数,我们把它当作基准数mid
    2 拿一个名字叫做high的水瓶子当作标记,从最右侧n-1号桶开始往左找,找到第一个比基准数mid小的数的时候,我们把这个数取出来,放在0号位置,把high放进这个水桶里,标记一下现在这个水桶空了。
    3 拿另一个名字为low的水瓶子当作标记,从刚才把high所在桶取出的数扔进的那个桶开始向右找,找到第一个比mid大的数,我们把low水瓶扔进去把数取出来,把这个数扔进high水瓶所在的桶里。
    4 从刚刚取出high的水桶开始向左找,找到第一个比基准数mid小的数,把high瓶子扔进去把数取出来,然后把数扔进low瓶子所在的桶里,把low瓶子取出来
    5 从刚取出low的桶开始向右找,找到第一个比基准数mid小的数,把low扔进桶,把数取出来放进high瓶子所在桶里,把high瓶子取出来
    6 从刚取出high的桶向左找,找到第一个比基准数mid大的数,把high扔进去,把数取出来放到low所在的桶里,把low拿出来
    7 从刚取出low的桶向右找,找到第一个比基准数mid小的数,把low扔进去,把数取出来放进high瓶子所在桶里,把high取出来
    .......
    我们发现从2到7 一直在做相同的工作,右侧的high水瓶子在一点一点向左推进,左侧的low水瓶子在不断向右推进
    最后 low和high两个水平碰面了,我们把mid基准数放到这个碰面的水桶里
    此时,mid左侧的数全都比mid小
    mid右侧的数全都比mid大

    然后,对mid 左侧所有的数当作一个新的数列,进行刚刚的排序
    再对mid右侧所有的数当作一个新的数列,进行刚刚的操作

    这样一直重复下去,数列就被排好了。

    我们简化一下刚刚的步骤:
    1 把第一个数当作基准数mid拿出来,0号位置空了
    2 从右侧向左找第一个比基准数小的数记录位置high,把数拿出来放到mid所在位置上,high位置空了
    3 mid右侧第一个向右找第一个比基准数大的数记录位置low,把数拿出来放到high空位上, 这时候左边low位置空了
    4 从high开始向左找,找到第一个比mid小的数high重新记录位置,把数取出来放到low位置上,high位置空了
    5 从low开始向右找,找到第一个比mid大的数low重新记录位置,把数取出来放到high位置上,low位置空了
    ....之后我们一直重复
    low一点点向右,high一点点向左
    当low和high碰头的时候,我们把mid放在碰头位置上
    这时候形成的局面就是,mid左侧的数全都比mid小,mid右侧的数全都比mid大
    然后我们把mid左侧所有数当成新的数列进行上述排序
    把mid右侧所有数当成新的数列进行上述排序

    这样一直重复下去,每次数列都被裁剪成更小的数列,一直到我们拿出第一个数做基准数,我发现右侧没有数左侧也没有数,这个数列排序结束了


    但是我们刚刚忽略了一个严重的问题!!!!我们把比mid小的数放在左侧,把比mid大的数放在右侧了!那和mid相等的呢????
    如果我们在两侧遇到mid相等的数,都移动到另一侧,实际上在程序中会陷入死循环,
    每次都拿出数放入对面的空位,对面从空位开始比较又扔了回来
    所以怎么办!!??
    其实,当两边碰到相等都不管,或者只有一侧进行移动到另一侧,另一侧不管,这样最终排序就能正确。
    如果我们都不管相等的数,再排子数列的时候,会判断出他是最大或者最小,再被移动到相应位置。

    时间复杂度:
    最坏时间复杂度: O(n^2) 我们假设拿到一个正序,每次拿到的基准数比较之后都没移动,所有的数都参与了比较,那就相当于每个数都跟其他所有数比了一次
    最优时间复杂度: O(n log n)
    我们粗略理解成每次把数列分成两半的话,一共会分多少次呢?就看长度n除以2 要除x次后n为1,x=log n
    每次分开的数列n个数每个都跟基准数比了一次 所以粗略看成 是 n log n
    稳定性: 不稳定
    从我们刚才探讨的,碰到和基准数相等的问题上就能看出快速排序不稳定



    我们上代码!!!
    在python中这样实现



    我们上代码!!!
    在python中这样实现
     1 #快速排序 传入列表、开始位置和结束位置
     2 def quick_sort( li , start , end ):
     3     # 如果start和end碰头了,说明要我排的这个子数列就剩下一个数了,就不用排序了
     4     if not start < end :
     5         return
     6 
     7     mid = li[start] #拿出第一个数当作基准数mid
     8     low = start   #low来标记左侧从基准数始找比mid大的数的位置
     9     high = end  #high来标记右侧end向左找比mid小的数的位置
    10 
    11     # 我们要进行循环,只要low和high没有碰头就一直进行,当low和high相等说明碰头了
    12     while low < high :
    13         #从high开始向左,找到第一个比mid小或者等于mid的数,标记位置,(如果high的数比mid大,我们就左移high)
    14         # 并且我们要确定找到之前,如果low和high碰头了,也不找了
    15         while low < high and li[high] > mid :
    16             high -= 1
    17         #跳出while后,high所在的下标就是找到的右侧比mid小的数
    18         #把找到的数放到左侧的空位 low 标记了这个空位
    19         li[low] = li[high]
    20         # 从low开始向右,找到第一个比mid大的数,标记位置,(如果low的数小于等于mid,我们就右移low)
    21         # 并且我们要确定找到之前,如果low和high碰头了,也不找了
    22         while low < high and li[low] <= mid :
    23             low += 1
    24         #跳出while循环后low所在的下标就是左侧比mid大的数所在位置
    25         # 我们把找到的数放在右侧空位上,high标记了这个空位
    26         li[high] = li[low]
    27         #以上我们完成了一次 从右侧找到一个小数移到左侧,从左侧找到一个大数移动到右侧
    28     #当这个while跳出来之后相当于low和high碰头了,我们把mid所在位置放在这个空位
    29     li[low] = mid
    30     #这个时候mid左侧看的数都比mid小,mid右侧的数都比mid大
    31 
    32     #然后我们对mid左侧所有数进行上述的排序
    33     quick_sort( li , start, low-1 )
    34     #我们mid右侧所有数进行上述排序
    35     quick_sort( li , low +1 , end )
    36 
    37 
    38 
    39 #ok我们实践一下
    40 if __name__ == '__main__':
    41     li = [5,4,3,2,1]
    42     quick_sort(li , 0 , len(li) -1 )
    43     print(li)

     

    如果后续有时间,我会再补一个c语言实现的快排上来!毕竟会python的伙伴太少了

    ok!!一大早上我就写了c语言版本的。因为c语言中如果不是全局数组,函数就不能修改他,为了让排序通用,我们利用指针!

    上代码!思想是一样的!要静下心来哦~

     1 #include "stdio.h"
     2 //c语言实现的快速排序算法
     3 //在c语言当中,如果不是全局数组,就不能在函数中直接改变数组里面的值,所以我们使用指针,传入待排序数列的开始和结尾元素指针
     4 void quickSort(int * start , int * end ){
     5     //每次都找到中间元素把两边子序列传进来再排序,如果发现传进来序列是一个元素,就不用执行了
     6     //当发现,一个子序列就一个元素,start和end就碰头了,就不用再排序了
     7     if( start>= end ){
     8         return ;
     9     }
    10 
    11     int * low = start;//用low标记待排序列开始位置,它向右寻找比基准数大的数的位置
    12     int * high = end;//用high来标记待排序列结束位置,它向左寻找比基准数小的数的位置
    13     int mid = *low;
    14 
    15 
    16     //low从左向右,找到大的数扔到右边
    17     //high从右向左,找到小的数扔到左边
    18     //当low和high碰头了,这个位置就是中间mid的位置,此时跳出排序循环
    19     while( low < high ){
    20         //low和high没碰头 并且high的数大于等于mid,high就左移
    21         //当high碰到第一个比mid小的数,就跳出循环,记录下这个数的位置
    22         while( low < high && mid <= *high ){
    23             high--;
    24         }
    25         //把high标记的比mid小的数扔到左边low的位置去
    26         *low = *high;
    27         //low和high没碰头 并且low的数小于等于mid,mid就右移
    28         //当low碰到第一个比mid大的数,就跳出循环,记录这个数的位置
    29         while( low < high && mid>= *low ){
    30             low++;
    31         }
    32         //把low标记的这个数扔到右边high的位置上
    33         *high = *low;
    34 
    35         //上述的过程我们一直做,在while循环当中,一直到low和high碰头了
    36     }
    37     //跳出循环说明low和high碰头了,这时候把mid扔到这个位置
    38     //此时mid左侧的数都比mid小 mid右侧的数都比mid大
    39     *low = mid;
    40     //把mid左侧的子序列扔进去递归继续排序, 从start到mid之前一个
    41     quickSort( start , low-1 );
    42     //把mid右侧的子序列扔进去递归继续排序  从mid之后一个到end
    43     quickSort( low+1 , end );
    44 }
    45 
    46 void main(){
    47     int i;
    48     int a[10]={10,9,8,7,6,5,7,4,3,1};
    49     quickSort(a,a+9);
    50     for(i = 0; i<10 ;i++){
    51         printf("%d ",a[i]);
    52     }
    53 }

     

     

    希望对朋友们有所帮助!欢迎大家发现错误与我交流

  • 相关阅读:
    Symantec Backup Exec Remote Agent 2010在Redhat Enterprise 6.6上启动问题
    RMAN冷备份异机还原
    ORACLE OLAP错误ORA-06512: at "SYS.OLAPIHISTORYRETENTION"
    expdp 报The value (30) of MAXTRANS parameter ignored错误的原因诊断
    ORA-01157 & ORA-01110
    INITIAL参数设置导致TRUNCATE TABLE不能降低高水位线案例
    ORA-19563: header validation failed for file
    ORACLE NUMBER类型Scale为0引发的问题
    ORA-04063: view "SYS.DBA_REGISTRY" has errors
    Linux 格式化扩展分区(Extended)
  • 原文地址:https://www.cnblogs.com/Lin-Yi/p/7308897.html
Copyright © 2011-2022 走看看