时间复杂度为 O ( n 2 ) O(n^2) O(n2)的排序算法:
- 名次排序:n(n-1)/2次比较排出名次,n次交换排好序;
- 选择排序:不断找出未排序元素中的最大值,将其移到数组末尾。找最大值的算法进行size-1次比较,总的比较次数为n-1+n-2+…+1=n(n-1)/2;
- 冒泡排序:和选择排序类似,只是找未排序元素最大值的方法是“冒泡策略”,即从数组头部开始,不断比较相邻元素的,如果左边的元素大于右边,则两者交换位置,直到把最大的元素交换到数组末尾。n次冒泡过程比较次数为n(n-1)/2
- 插入排序:不断往有序数组里插入新的值,从单元素数组开始。每次插入时间复杂度为 O ( n ) O(n) O(n),插入n次,总的复杂度为 O ( n 2 ) O(n^2) O(n2)。
时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)的算法
1.堆排序:首先用待排序的元素初始化一个大根堆,时间复杂度为
O
(
n
)
O(n)
O(n),每次删除大根堆的根元素,并把剩余的元素重排,时间为
O
(
l
o
g
n
)
O(logn)
O(logn),执行n次这样的操作,总的时间复杂度为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
2.归并排序:(二路归并排序)利用分而治之的思想,把每个待排序的数据段,分为大致相等的两段,分别进行排序,然后再把排序后的结果归并起来,每一小段都可以继续分成两端再归并,直到分成单个元素,从而实现分而治之。算法复杂度为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。是一种稳定排序。
3.快速排序:(三值取中,或取最左端元素为支点)同样利用分而治之的思想,把待排数据分为三段,中间一段只有一个元素,左边段的元素都小于等于中间段,右边的元素都大于等于中间段,然后每一小段继续这样划分。算法复杂度为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)**。是一种不稳定排序。**普通的快排再序列本身有序的情况下,左端或有段总是空的,这是最坏情况,时间复杂度为
O
(
n
2
)
O(n^2)
O(n2);三值取中解决了这个问题,最坏情况下的时间复杂度为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
时间复杂度为 O ( n ) O(n) O(n)的排序算法
1.箱子排序:将要排序的元素的所有range个取值可能,设计为range个箱子,从输入个数为n的链表的头部开始,把每个元素输出到对应值的箱子里,相同的元素在同一个箱子里,采用链表储存。最后再从箱子里把元素收集起来,即可得到一个有序的链表。把n个元素放到箱子里,时间为
Θ
(
n
)
\Theta(n)
Θ(n),把箱子里的元素收集起来,时间最为
Θ
(
r
a
n
g
e
)
\Theta(range)
Θ(range),总的时间复杂度为
Θ
(
n
+
r
a
n
g
e
)
\Theta(n+range)
Θ(n+range)。当range和n是一个数量级的时候,时间复杂度为
Θ
(
n
)
\Theta(n)
Θ(n)
此方法适用于箱子的range小于等于和输入元素n的情况。如果输入了10个范围在0-999内的数,即n=10,range=1000,那这个方法会很耗时。
2.基数排序:这是改进版的箱子排序。先将要排序的数根据一个基数r分解之后再对分解得到的每一部分进行箱子排序,例如,输入了10个范围在0-999的数,我们以r=10为基数,将输入的数分为个位,十位,百位,然后对于每一位分别进行箱子排序。每一位的范围都是r,时间复杂度为
Θ
(
r
+
n
)
\Theta(r+n)
Θ(r+n)。当基数和输入数的数目在同一数量级时,时间复杂度变为
Θ
(
r
)
\Theta(r)
Θ(r)。
此方法适用于输入的range和远远大于n的情况。然后将range以r=
Θ
(
n
)
\Theta(n)
Θ(n)(基数和输入的数的数目在同一数量级时)为基数分解后程序的时间复杂度最低。
需要注意的是,虽然插入排序等时间复杂度为
O
(
n
2
)
O(n^2)
O(n2)在渐进情况下耗时很多,但在n比较小的时候性能反而比
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)复杂度的要好。
所以STL设计的sort排序算法,把这两种综合了起来,合并成一个分段函数,当n<c(某个常数)时,采用插入排序,当n>c时采用堆排序。(不稳定排序)
stable_sort在n<c时,采用插入排序,n>c时采用归并排序(稳定排序)