• 面试突击(1)——数据结构基础,排序


    1 排序

    (转载,有一定程度的精简)

    在待排序的文件中,若存在多个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序保持不变,该排序方法是稳定的,若具有相同关键字的记录之间的相对次序发生变化,则称这种排序方法是不稳定的。


    选择排序:包括简单选择排序和堆排序。

    1 简单选择排序

            示例:假设给定数组A[1......6]={ 3,5,8,9,1,2 },我们来分析一下A数组进行选择排序的过程

            第一趟:i=1,index=5, a[1] 和 a[5] 进行交换。得到序列:{ 1,5,8,9,3,2 }

            第二趟:i=2,index=6, a[2] 和 a[6] 进行交换。得到序列:{ 1,2,8,9,3,5 }

            第三趟:i=3,index=5, a[3] 和 a[5] 进行交换。得到序列:{ 1,2,3,9,8,5 }

            第四趟:i=4,index=6, a[3] 和 a[5] 进行交换。得到序列:{ 1,2,3,5,8,9 }

            第五趟:i=5,index=5, 不用交换。得到序列:{ 1,2,3,5,8,9 }

           (6-1)趟选择结束,得到有序序列:{ 1,2,3,5,8,9 }

     描述:给定待排序序列A[ 1......n ] ,选择出第i小元素,并和A[i]交换,这就是一趟简单选择排序。

    性能分析:不论最坏还是最佳情况,比较次数都是一样的,所以简单选择排序平均时间,最坏情况,时间复杂度O(n^2)。

                        简单选择排序是稳定的排序。


    2 堆排序——相对于选择排序来说,主要目的是减少比较次数

    堆:一棵完全二叉树且满足性质:所有非叶子结点的值均不大于或均不小于其左、右孩子结点的值。

    过程描述:1、建堆  2、将堆顶记录和堆中最后一个记录交换  3、筛选法调整堆,堆中记录个数减少一个,重复第2步。整个过程中堆是在不断的缩减。

    堆排序是不稳定的原地排序算法。



    这个堆可以看成是一个一维数组A[6]={9,8,5,3,1,2},那么相应的这个数组需满足性质:A[i]<=A[2*i] && A[i]<=A[2*i+1] 。其中A[i]对应堆中的非叶子结点,A[2*i]和A[2*i+1]对应于左右孩子结点。并且最后一非叶子结点下标为[n/2]向下取整。

    调整:

    把9跟2互换,同时把a[6]这个结点从堆中去掉,于是得到下面这个完全二叉树。


     A[5]={2,8,5,3,1}

    比较根结点和左、右孩子的值,若根结点的值不小于孩子结点的值,说明根结点并没有破坏堆的性质,不用调整;若根结点的值小于左右孩子结点中的任意一个值,则根结点与孩子结点中较大的一个互换,互换之后又可能破坏了左或右子树的堆性质,于是要对子树进行上述调整。这样的一次调整我们称之为一次筛选。

    A[5]={2,8,5,3,1}

    A[5]={8,2,5,3,1}

     A[5]={8,3,5,2,1}


    建堆:

    A[6]={3,5,8,9,1,2},怎样使它变成一个堆呢?


    仔细想一想筛选法的前提条件是什么:根结点的左右子树已经是堆。那么这棵树中哪个结点的左右子树是堆呢,很自然的发现是最后一个非叶子结点,所以我们在这里需要自下而上的调整这个完全二叉树。


    A[6]={3,9,8,5,1,2}


     A[6]={9,3,8,5,1,2}

    时间分析:堆排序时间=建堆时间+调整堆时间。从上文中知道建堆时间复杂度为O(n*log2n)。筛选法调整堆(maxHeap函数)时间O(log2n),总共循环了n-1次maxHeap函数,所以调整堆时间复杂度为O(n*log2n)。得出堆排序时间复杂度O(n*log2n)。


    堆排序的本质和选择排序的本质是一样的。选择一个待排序序列中的最小(大)值,这就是选择排序的本质。


    插入排序,包括直接插入排序、折半插入排序、表插入排序、希尔插入排序

    3 直接插入排序

    描述:给定待排序序列A[ 1.....n ],现假设A[1...i]已经有序,那么我们取出A[i+1]插入到序列A[1...i].这样有序序列记录数就增加了1.如此重复上述操作,不断取出记录插入有序序列,直到A[n]插入到有序序列,排序完成。
    时间复杂度:最佳情况下(待排序序列有序),比较次数和移动次数时间为o(1),所以时间复杂度为o(n).在最坏情况下(待排序序列逆序)和平均时间均为o(n^2).

    4 折半插入排序

    描述:在直接插入排序过程中,我们是把一个记录插入到有序序列中,至于要插入到有序序列中的哪个位置呢?采用的是顺序查找确定插入的位置。显然对于有序序列,折半查找的效率要高,所以在寻找插入位置时可以用折半查找。折半查找主要分为三步:1、查找要插入的位置  2、移位  3、把记录插入相应的位置。
    时间复杂度:折半插入排序是对直接插入排序的一种改进,这种改进只考虑了关键字比较次数,并没有减少移位次数,所以平均时间和最坏情况下(待排序序列逆序)时间复杂度o(n^2),如果记录数量很大的话,这两种情况下是优于直接插入排序。再来看一下最佳情况(待排序序列有序),此时关键字比较次数并不为o(1),时间复杂度为o(n*log2n)。(其中折半查找时间复杂度o(log2n),这个在以后写查找的时候再分析,这里不做详细讲解。)。所以在记录数较小、待排序序列基本有序情况下直接插入排序优于折半插入排序。此外,折半插入排序是不稳定的原地排序,实现起来也较复杂。

    5 希尔排序(不稳定)

    描述:上述两种排序都是只考虑减少关键字比较次数或者只考虑减少关键字移动次数。有没有别的改进办法呢?我们注意到,直接插入排序适合于记录数较少、基本有序的情况。于是我们可以先将整个待排序序列分割成若干子序列分别进行直接插入排序,整个序列基本有序时,再对序列进行一次直接插入排序。这就是希尔排序。
    时间复杂度分析:希尔排序的执行时间依赖于增量序列。
    希尔排序的时间性能优于直接插入排序的原因:
    1)当文件初态基本有序时,直接插入排序所需的比较和移动次数均较少
    2)当n值较小时,n和n^2的差别也较小,即直接插入排序的最好时间复杂度O(n)和最坏时间复杂度O(n^2)差别不大。


  • 相关阅读:
    第13篇-通过InterpreterCodelet存储机器指令片段
    第12篇-认识CodeletMark
    第11篇-认识Stub与StubQueue
    第10篇-初始化模板表
    第9篇-字节码指令的定义
    Java虚拟机漫漫学习路,我终于悟了
    Unity真机连接profiler
    提取图片中文字方法
    登录锁定个人收藏代码
    java判断密码是否为复杂类型(同时包含大小写和数字)
  • 原文地址:https://www.cnblogs.com/suzhou/p/3638999.html
Copyright © 2020-2023  润新知