rambo

排序算法(二)计数与插入

计数排序

输入增强:对问题的部分或全部输入做预处理,然后将获得的额外信息进行存储,以加速后面问题的求解。(以空间换时间)。

计数排序:针对待排序列表中的每一个元素,算出列表中小于该元素的元素个数,并将结果记录在一张表中。这个“个数”指出了该元素在有序表中的位置。当几个元素相同时,需要做一定的修改。

算法描述:计数排序假设n个输入元素的每一个都是在0到k区间内的一个整数。当k=O(n)时,排序的运行时间为\Theta(n)。假设输入是一个数组A[1,...,n],需要两个额外的数组:B[1,...,n]存放排序的输出,C[0,...,k]提供临时存储空间。

这个其实应该叫做分布计数排序。

伪代码:

QQ截图20150626195316

如果n个元素都是互异的,对于每一个A[j]值来说,C[A[j]]-1就是A[j]在输出数组中最终正确位置。因为所有元素可能并不是互异的,每将一个值放入数组output中以后,将C[A[j]]的值减1。这样,当遇到下一个值等于A[j]的输入元素时,该元素可以直接放到输出数组output中A[j]的前一个位置。

算法复杂度分析:第一次for循环所花时间为\Theta(k),第二次循环所花时间为\Theta(n),第三次循环所花时间为\Theta(k),第四次循环所花时间为\Theta(n)。总的时间代价为\Theta(n+k)。在实际工作中,当k=O(n)时,运行时间为\Theta(n)

给定一个大文件,内含5亿个整数,每个整数都属于1-9999999之间。请设计方案,对这些元素进行排序,并将排序结果写成文件输出。可用内存不超过2G。

注意到元素一共有5亿个,因此,文件中肯定存在重复元素。可以考虑使用CountingSort(计数排序),统计每个元素出现的次数。

QQ截图20150626202847

插入排序(升序)

思想:对于数组A[0,...,n-1]排序假设对较小数组A[0,...,n-2]已经排好序,显然我们要做的就是在这些有序的元素中为A[n-1]找到一个合适的位置,然后把它插入。一般来说,即从右到左扫描这个这个有序的子数组,直到遇到第一个小于等于A[n-1]的元素,然后把它插在这个元素的后面。这里我们采用自底向上的迭代算法。

伪代码:

QQ截图20150702212051

说明:首先排序的是A[0,1]这个子数组,如(d)图所示就很好的表示了A[j+1]<--A[j],j<--j-1这个过程。如果A[j]<=v,即找到了那个插入的位置,则在这个元素后面这个位置插入v值。值得注意的是上述伪代码中其实有一个很大的BUG,当j=-1时,while会做A[j]>v这个判断,这时候就会发生异常。所以最好还是用for循环。

在每次for循环的迭代开始时,包含元素A[0,...,j-1]的子数组构成了当前排好序的左手中的牌,剩余的子数组A[j+1,n-1]对应于仍在桌子上的牌。

算法导论中的示意图(下标从1开始)如下:

charupaixu算法复杂度分析:算法中的基本操作就是键值比较A[j]>v。在最坏情况下,A[j]>v的执行次数达到最大,即对于每个j=i-1,...,0,都要执行一次,这种情况在对于每一个j都有A[j]>v=A[i]时发生。注意,在最坏的情况下,有A[0]>A[1](当i=1时),A[1]>A[2],...,A[n-2]>A[n-1](当i=n-1)。即最坏输入是一个严格递减的数组,对于这种输入的键值比较次数为

QQ截图20150702214101

值得一提的是,在最坏情况下,插入排序和选择排序的键值比较次数是完全一致的。

最好的情况下,即在外部循环的每次迭代中,A[j]>v只执行一次。当且仅当对于每一个i=1,...,n-1,都有A[i-1]<=A[i]。即输入数组已经按照升序排列。对于有序的数组,键值比较次数为

QQ截图20150702214415

http://www.cnblogs.com/microsoftmvp/p/4592461.html

从FILE*对象中Load数据

 相关问题:

1)假设 A[1..n] 是一个有n个不同数的数组。若 i<j and A[i]>A[j], 则the pair (i,j) 被称作A的一个逆序对。
插入排序的运行时间与输入数组中的逆序对的数量之间是什么关系?

答:在插入排序进行时,数组分为两部分:已排序部分和待排序部分。每次将待排序部分的第一个元素插入到已排序部分时,需要找出其插入的位置,并把这之后的已排序元素依次后移。插入排序的运行时间与插入排序时的总的比较次数相关,而总共的比较次数与总的逆序对的个数相同。因此两者成正比。在插入排序的过程中,插入A[j]需要比较的次数和已排序的A[1...j-1]中比A[j]大的元素个数相同,这等价于逆序对的个数。
这样来理解,内层循环解决了逆序对后,这个过程不会引入新的逆序对,每个外层循环只会解决m个逆序对。我们可以算出逆序对有9组,而内层比较次数总和确实为9次。
运行时间为\Theta(n + d)。d为总的逆序对个数,n为外层循环。

QQ截图20150702212051

 

对链表插入排序

链表与数组进行插入排序的区别在于:第一,在插入每个新纪录时,必须从头至尾查找已经有序的记录,这样就会导致算法在处理已经有序的记录时会变得最慢,而在处理逆序记录时则会变得最快。假设单链表只能从头至尾进行遍历,不可避免。第二,

 

希尔排序

希尔排序是插入排序的一个变体。插入排序之所以缓慢的原因是:它一次只把一个项目称过一组有序的记录。就是说,如果记录离他们的最终位置很远,则只有在经过多次交换后才能正确地定位它们。解决:在排序的早期阶段允许记录跳转较大的距离。

基本思想:把记录分成几个交替的组,然后使用插入排序算法对每个组进行排序。下面的例子非常清晰的阐述了 这个 基本思想。
QQ截图20150823025127

 

为什么说这是对普通的插入排序的改进呢?我们注意观察A的移动。当它从完全错误的位置开始移动时,在前两次移动的每一次移动中,它将跳过相距其位置一般的距离,只需要三次交换即可到达其目标位置。

这种每隔h个记录选择一个记录以便反复再分为交替记录集的方式称为希尔排序。选择合适的h值序列需要做大量的工作。编程时采用以下模式来确定h的值序列:

h_{1}=1,n=待排序的记录数

h_{s+1}=3h_{s}+1,当h>n/9时停止。

讨论:虽然希尔排序不具有像插入排序或冒泡排序处理已经有序的记录时那样引人注目的速度,但是在处理逆序或随机顺序的记录时也不会遭受显著的速度降级。实际上,希尔排序的运行时间对输入数据相当不敏感。希尔排序的唯一负面特性就是:它是一种不稳定的排序。希尔排序成为几乎任何情况下的一个优秀的选择。