数据结构之排序

排序
排序稳定性
假设Ki=Kj(1<=i<=n,1<=j<=n,i!=j),且在排序前的序列中ri领先于rj。如果排序后ri仍领先于rj,则称所用的排序方法时稳定的;反之,若可能使得排序后的序列中rj领先ri,则称所用的排序方法是不稳定的。

image

内排序与外排序
内排序是在排序整个过程中,带排序的所有记录全部被放置到内存中,外排序是由于排序的记录个数太多,不同时放在内存中,整个排序过程需要在内外存之间多次交换数据才能进行。
根据排序过程中借助的主要操作,将内排序分为:插入排序、交换排序、选择排序和归并排序。
冒泡排序属于交换排序;
选择排序是通过每一轮比较后挑选出最小的,再与对应位置进行交换,而不是每次比较都进行交换;
插入排序是将一个记录插入到已经排好序的有序表中,得到一个新的,记录增1的有序表;
希尔排序主要是将待排序序列分块完成大致有序,再将块缩小逐步完成排序
希尔排序的关键不是随便分组后各自排序,而是在分组时就应经在排序,将相隔的某个增量的记录组成一个子序列,实现跳跃式移动,使得排序效率增高
堆结构&堆排序
堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如果按照层序的方式给结点1开始编号,结点编号之间满足如下关系:
image

堆排序算法
堆排序就是利用堆(假设利用大顶堆)进行排序的方法。其基本思想:
将待排序的序列构造成一个大顶堆。整个序列的最大值就是堆顶的根结点。将其移走(将其与对数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中次大值,反复进行,直到得到最终有序序列。
堆排序的时间复杂度:
堆排序的主要时间消耗在重建堆和筛选的时间上
在重建堆的时候,将结点与其孩子进行比较和必要的互换,对于每个非终端结点,最多比较两次比较和互换操作,构建堆的时间复杂度为O[n]
在进行排序过程时们需要进行n-1次的取堆顶记录,第i次取到堆顶记录重建堆需要O[logi]的时间(完全二叉树的结点到根结点距离为[logi]+1),因此整体的时间复杂度为O[nlogn]

归并排序
归并在数据结构中的定义是将两个或两个以上的有序表组合成一个新的有序表。
归并排序(Merging Sort)就是利用归并的思想实现排序的方法。其原理是:假设初始含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2]个长度为2或1的有序子序列,再两两归并,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。
一趟归并排序需要将原数组的n个元素进行两两归并,将结果放到排序后的数组中,需要将待排的序列中的所有记录扫描一遍,耗费O[n]时间,同时由完全二叉树的深度,整个归并排序需要logn次,因此整体的时间复杂度为O[nlogn]
而在空间复杂度上,归并排序在归并过程中需要与原始记录同样数量的存储空间存放归并结果以及递归深度为logn的栈空间,因此,总的空间复杂度为n+logn
同时,归并排序中由于存在两两比较,不存在跳跃,因此归并排序是一种稳定排序算法。

快速排序
快速排序与冒泡排序一样属于交换排序类。快速排序的实现,增大了记录的比较和移动的距离,将关键字较大的记录从前面直接移动到后面,关键字较小的记录从后面直接移动到前面来,减少总的比较次数和移动交换次数。
快速排序的基本思想:
通过一趟排序将待排序记录分割成可独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
交换排序最好的情况下,中枢轴的分布正好是类似完全二叉树的结构,其时间复杂度为O[nlogn]
而如果是类似斜树情况下(最坏情况),中枢轴每次都在最前端,需要执行n-1次递归调用,因此比较次数为n-1,n-2....1的求和,即为n(n-1)/2,其时间复杂度为O[n^2]
空间复杂度,主要是由于递归调用会造成相应的栈空间使用,最好情况下,递归的深度为logn,空间复杂度为O[logn],最坏情况下,需要进行n-1次递归,因此空间复杂度为O[n]
**快速排序优化: **

  1. 中枢轴的选取
    基础的快速排序中,在对子序列进行分割找到中枢轴位置时,中枢轴开始都是选取固定的位置,因此如果固定位置处的中枢轴的关键字大小不合适会造成性能瓶颈,因此在中枢轴的选取上可以采取三数取中。取三个关键字先进行排序,将中间数作为中枢轴,一般取左端,右端和中间三个数。

  2. 优化不必要的交换
    在寻找中枢轴的位置时,不采用Swap交换的形式,而是采用替换的形式

pivotKey = L->arr[low];
 L->arr[0] = pivotKey;
 while (lowarr[high] >= pivotKey)
    high--;
    //Swap(L, low, high);
    //使用替换代替交换,节省部分性能
    L->arr[low] = L->arr[high];
    while (low < high&&L->arr[low] <= pivotKey)
    low++;
    //Swap(L, low, high);
    //使用替换代替交换,节省部分性能
    L->arr[high] = L->arr[low];
 }
 L->arr[low] = L->arr[0];
 return low;   

3.优化小数组时的排序方案
当使用小数组时,可以使用直接插入排序,而不是继续使用快速排序来提高整体性能,因此可以检测排序长度,当长度大于某个定值时采用快速排序,而小于某个定值时,直接使用插入排序。

4.优化递归操作
通过减少递归次数来提高性能

//减少递归次数优化整体性能
 while (low < high) {
    pivot = Partition(L, low, high);
    QSort(L, low, pivot - 1);
    low=pivot+1;
 }
 //pivot = Partition(L, low, high);
 //QSort(L, low, pivot - 1);
 //QSort(L, pivot+1, high);   

你可能感兴趣的:(数据结构之排序)