基本排序排序算法

时空复杂度:

冒泡排序:时间O(n^2),额外空间O(1)

插入排序:时间O(n^2),额外空间O(1)

选择排序:时间O(n^2),额外空间O(1)

基数排序:时间O(k*n)(k=logN_max),额外空间O(n)(临时存储)+O(B)(记数,B为基的大小)

记数排序:时间O(n+k),额外空间O(k)

希尔排序:时间O(n*logn^2),额外空间O(1)

快速排序:时间O(n*log(n)),额外空间O(logn)(递归栈)

归并排序:时间O(n*log(n)),额外空间O(n)(临时数组)+O(logn)(递归栈)

堆排序:  时间O(n*log(n)) 

使快速排序退化的例子:

n, n-1, n-2, n-3.......3,2,1,快速排序具有O(n^2)的复杂度。

优化方法:随机选择划分元素,而不是固定选择最左面的元素。

对归并来说,一个很不理想的例子:

1,2,3,…… , n

对有序序列还是需要O(nlogn)的时间复杂度。

优化方法:找到序列的连续自增段,把这样的自增段进行合并。

稳定排序和不稳定排序

稳定的排序算法:冒泡排序、插入排序、基数排序、记数排序、归并排序

非稳定排序算法:希尔排序、选择排序、快速排序、堆排序

实际上稳定排序可以实现成非稳定的,看具体的实现过程。有些同学误以为自己写了一个冒泡排序就是稳定的,注意比较的时候是">=" 还是">"。

内部排序与外部排序

http://zh.wikipedia.org/zh/%E5%A4%96%E6%8E%92%E5%BA%8F

内部排序:内部排序是指待排序列完全存放在内存中所进行的排序过程,适合不太大的元素序列。

外部排序:外部排序指的是大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次装入内存,需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的。最常用的外部排序是多路归并排序。对于外部排序,影响速度的最主要因素为访问磁盘的次数,所以设计外部排序算法时,尽可能减少访问磁盘的次数。

对于外部排序多路归并算法的时间复杂度进行简要分析。

设有n个数,分成k段,之后利用m路归并 。

t1 = n*log(n/k) 每段分别排序  n*log(n/k)。

t2 = n*logk    每趟对n个数合并(每个数每趟只操作一次),一共logk趟,

t3 = n * (T_read + T_write) * logk 从文件中读取和写入数字花费的时间,

设每读取一个数的耗时为T_read,每次写一个数耗时为T_write,

T(n) = t1 + t2+t3 = n*log(n/k) + n*logk + n*(T_read+T_write)*logk ,其中logk是以m为底的。

所以,为了减少访问磁盘的次数,必须增大基m,但是m并不是越大越好,如果越大,则在m个数中选择最大值会越慢,所以一般都选m=9。

逆序数问题

对于序列 x1,x2,x3.......xn

若存在i>j,且满足x[i] < x[j],则称(x[i],x[j])构成一个逆序对, 一个序列所有的逆序对的数目叫做这个序列的逆序数。

例如序列 :5 4 3 2 1

逆序数为 1+2+3+4 = 10

利用归并排序可以快速的求得一个序列的逆序数。

寻找第K大数算法

问题1:给出一个序列,求这个序列第K大的数(离线)

比较通用的算法:

(1) 利用快速排序的划分。每次可以省去一部分的数进行查找。

设下一次的查找百分比为q<1,

时间 T(n) = n + T(n*q)

                     = n + n*q + T(n*q^2)

                     = n + n*q + n*q^2 + T(n*q^3)

                     ......

                     = n(1 + q + q^2 + q^3 + ......)

                     = n(1*(1-q^k)/(1-q))

当q=1/2时, T(n) = 2n = O(n)。

这种方法最坏情况下的时间复杂度是O(n^2)。

那么有没有能够保证最坏时间复杂度是O(n)的算法吗?有

(2) 最坏时间下是O(n)复杂度的算法

http://en.wikipedia.org/wiki/Selection_algorithm 或参见<<算法导论>> P190-P191

针对普通的划分和随机化划分的缺点,对选择划分数进行改进:选择中位数的中位数进行划分。

假设函数Find(A, k, n)返回的是A数组中第k大的元素,新的查找算法如下:

1:将输入数组的n个元素划分为n/5组,每组5个元素,至多有一个组的元素数目小于5。

2:寻找n/5个组的每个组的中位数。对每个组的5个元素利用插入排序,之后选择中位数。

3: 对第2步找到的n/5个中位数递归调用Find函数,找出其中位数x。

4:利用中位数x对输入数组进行划分。并得出x的排位。

5:如果第四步得到的x的排位为k,则返回x。如果x的排位大于k,则在低区进行递归查找,否则在高区进行查找。

因为x是中位数,除去x本身所在的组和那个元素数目小于5的组不算,大于x的元素数目至少为3*(1/2)*(n/5 - 2) = 3*n/10 - 6。

同理小于x的元素数目至少为3*n/10 - 6。那么第4步最多有n-(3*n/10-6) = 7*n/10+6个元素参加下次的递归查找。

设Find(A, k, n)花费时间为T(n)。

则T(n) = t1 + t2 + t3 + t4 + t5。

t1 = O(n)    (分成n/5组)

t2 = O(n)    (每组进行选择可以看做O(1)时间花费)

t3 = T(n/5)   (对n/5个中位数递归选择)

t4 = O(n)     (根据x进行划分)

t5 = T(7*n/10 + 6)  (下一次递归选择)

T(n) = T(n/5) + T(7*n/10 + 6) + O(n)

最后可推算出T(n)=O(n),但是个人认为常数比较大。

K非常小时情况下的选择第K大元素算法:

利用冒泡或者选择排序,时间复杂度O(K*n)。

 

 

原文地址:https://www.cnblogs.com/haolujun/p/2699612.html