该文章是关于数据结构部分排序的总结,包括各种排序方法的时间和空间复杂度的分析,主要从直接插入、交换(冒泡、快速)、选择(直接选择、堆排序)和归并四类来分析。
直接插入:
依次将每个记录插入到一个已排好序的有序表中,从而得到一个新的、记录数增加1的有序表。具体:插入第i个记录时,前i-1已经排好序,此时将第i个记录的关键字和第i-1,i-2比较,从而找到插入位置插入位置及其后记录依次向后移动。
举例:9 6 15 8 11
复杂度: 直接插入最好情况下每趟只需做一次比较且不用移动元素,因此n个元素总比较次数n-1,总移动0次;最坏情况下进行第j趟排序时与前面每个记录都有比较,双重循环,此时是关于n^2的式子;又排序过程中仅需一个元素的辅助空间,空间复杂度O(1),且是稳定排序。
交换:
冒泡:首先将第一个记录键值和第二个记录键值比较,如果R[1].key>R[2].key,则交换,然后继续比较第二个和第三个,比较n-1次后完成最大记录在n的位置,此为第一趟起泡;然后进行第二次起泡完成最大记录在n-1位置,重复此过程直至没有进行记录交换的操作时结束。
举例:9 6 15 8 11
冒泡排序最理想情况,一组排序好的序列,只需比较n-1次,完成一次起泡即可,时间复杂度为O(n),最坏情况,一为n个元素逆序列,每趟比较次数为n-1,n-2…1,所以O(n^2);排序过程仅需一个元素的辅助空间用于元素交换,空间复杂度O(1),且是稳定排序。
快速:通过一趟排序将待排序记录分为独立两部分,其中一部分记录关键字不小于另一部分关键字,然后再对两部分继续快速排序。做法:附设两指针i和j,初值分别指向第一个记录和最后一个记录,通常假设第一个记录值作为关键字key,首先从j所指位置向前搜索,找到第一个关键字小于key的记录并与之交换,然后从i所指位置向后搜索,找到第一个关键字大于key的记录并与之交换,重复这两步直至i与j相等。
选取第一个记录9为关键字,完成第一趟排序后,再对9左右两部分快速排。
时间复杂度:通过主定理:T [n] =
aT[n/b] + f (n),其中 a >= 1 and b> 1 是常量 并且 f(n) 是一个渐近正函数, 为了使用这个主定理,您需要考虑下列三种情况:
快速排序的每一次划分把一个 问题分解成两个子问题,其中的关系可以用下式表示:T[n] = 2T[n/2] +O(n) 其中O(n)为PARTITION()的时间复杂度,对比主定理, T [n] =aT[n/b] + f
(n),我们的快速排序中:a = 2, b =2, f(n) = O(n)
选择:
直接选择:在第i次选择操作中,通过n-i次键值比较,从n-i+1个记录中选择键值最小的记录,并和第i(I<=i<=n-1)个记录交换。
举例:9 6 15 8 11
复杂度分析:直接选择包含两层嵌套for循环,时间复杂度为O(n^2),且是不稳定的。
堆排序:对一组待排序记录建立初始堆(建堆过程:将待排序关键字放到一课完全二叉树各个结点中,从n/2开始筛选,逐步把n/2-1,n/2-2…1为根的子树筛选成堆)排序过程中先和最后一个结点比较,每比较交换一次都有重新建堆,直至完成前两个结点的比较,基于最小堆的堆排序输出由大到小的序列。
举例:9 6 15 8 11
放在一棵二叉树中为:
建堆:从n/2即第二个结点6开始筛选,然后筛选第一个结点,建好堆如下:
堆排序过程:堆顶元素6和最后结点11比较,交换,重新构建堆:
然后堆顶元素8和倒数第二个结点比较,交换之后重新构建堆,直至前两个结点比较交换后结束。
时间复杂度:包括筛选和堆比较两部分。那么要执行多少次筛选呢?每一次筛选根结点都往下沉,所以筛选次数不会超过完全二叉树的深度:([log2n]向下取整+1),其中n为结点个数,2为底数,即时间复杂度为O(log2n),堆比较是堆顶结点和最后一个结点开始比直至第二个结点,呈线性,所以为O(n),所以时间复杂度O(nlog2n)。
归并(以二路归并来说):反复讲两个有序文件归并呈一个有序文件的排序方法。
举例:9 6 15 8 11
时间复杂度O(nlog2n),且是稳定的。关于时间复杂度分析不是很理解,欢迎交流。