1. 排序的特殊情况
所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减排列起来的操作。排序是计算机内经常进行的一种操作,其目的是将一组无序的记录序列调整为有序的记录序列。
归并排序、堆排序、快速排序因为其较好的时间复杂度广泛使用。但是 ,这些算法都存在一些缺陷。如应用广泛的快速排序,因为其不能满足稳定性要求,在多列表格排序中会出现不对应的问题。
改进方法是混合排序(Hybrid Sorting Algorithm),如:
- 内省排序:结合 快速排序和堆排序
- 蒂姆排序:结合 归并排序和插入排序
蒂姆排序就是利用了数据的这个特性来减少排序中的比较和数据移动的,它的大致思想如下。
- 找出序列中各个递增和递减的子序列,二分查找寻找插入位置,随后将这些有序子序列放入临时存储空间(堆栈)中
- 按照规则合并这些块
- 顺序扫描方式同归并排序算法
- 采用的是一种跳跃式(galloping)预测的方式
时间复杂度O(nlogn)
,空间复杂度O(n)
python实现
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
# write code here
if tinput==[] or len(tinput)<k:
return []
tinput.sort()
return tinput[:k]
2. 排序算法的复杂度
Q:为什么排序算法的复杂度不可能超过 O ( n log n ) O(n\log{n}) O(nlogn)?
定义——序列的比较
有两个序列, 序列 a : [ a 1 , a 2 , … , a i , … , a N ] 序列a:[a_1,a_2,…,a_i,…,a_N] 序列a:[a1,a2,…,ai,…,aN]和 序列 b : [ b 1 , b 2 , … , b i , … , b N ] 序列b:[b_1,b_2,…,b_i,…,b_N] 序列b:[b1,b2,…,bi,…,bN] ,有
- 前 N N N 个元素相同,即 a 1 = b 1 , a 2 = b 2 , … , a i − 1 = b i − 1 a_1=b_1, a_2=b_2,…, a_{i−1}=b_{i−1} a1=b1,a2=b2,…,ai−1=bi−1
- 第 i i i 个元素, a i ≤ b i a_i \le b_i ai≤bi
则 a 序列 < b 序列 a序列 < b 序列 a序列<b序列
对于任意一个序列 a 1 , a 2 , … , a N a_1,a_2,…,a_N a1,a2,…,aN,假如随意排列其中的元素,可以排出很多种序列,则这些排列中,最小的序列是将其中每一个元素从小到大排好序的那个序列。在所有可能的排列组合中,通过元素的比较挑出最小的一个,就是排序。
Q:一个N个元素的数组 [ a 1 , a 2 , … , a i , … , a N ] [a_1,a_2,…,a_i,…,a_N] [a1,a2,…,ai,…,aN],排序需要进行多少次比较?
- 两个序列的比较:
A
1
:
[
a
1
,
a
2
,
…
,
a
i
,
…
,
a
j
,
a
N
]
A_1:[a_1,a_2,…,a_i,…,a_j,a_N]
A1:[a1,a2,…,ai,…,aj,aN] 和
A
2
:
[
a
1
,
a
2
,
…
,
a
j
,
…
,
a
i
,
a
N
]
A_2:[a_1,a_2,…,a_j,…,a_i,a_N]
A2:[a1,a2,…,aj,…,ai,aN]
若 a i ≤ a j a_i \le a_j ai≤aj,则 A 1 < A 2 A_1 < A_2 A1<A2; 若 a i > a j a_i > a_j ai>aj,则 A 1 > A 2 A_1 > A_2 A1>A2
即 一次比较即可区分两个序列大小 - 两次比较最多可以区分几个序列? 四次 ⟹ \Longrightarrow ⟹ k k k次比较,可区分 2 k 2^k 2k种不同序列
- M 种序列,需要 log M \log M logM 次比较
- N 个元素的序列,排出 N ! N! N! 种序列,区分这些序列,得到最小序列,需要 log N ! \log N! logN! 次比较。
计算logN!需要使用斯特林(Stirling)公式:
l
n
N
!
=
N
l
n
N
−
N
+
O
(
l
n
N
)
lnN!=NlnN−N+O(lnN)
lnN!=NlnN−N+O(lnN)。即可以得出
l
o
g
N
!
=
O
(
N
l
o
g
N
)
logN!=O(NlogN)
logN!=O(NlogN)的结论。
此时估算出的是排序所需要进行比较的次数的下限。即,任何排序算法的复杂度不会低于
O
(
N
l
o
g
N
)
O(NlogN)
O(NlogN)。
3. 思考题
思考题1.4.1 赛跑问题(GS)
假定有25名短跑选手比赛争夺前三名,赛场上有五条赛道,一次可以有五名选手同时比赛。比赛并不计时,只看相应的名次。假设选手的发挥是稳定的,也就是说如果约翰比张三跑得快,张三比凯利跑得快,约翰一定比凯利跑得快。最少需要几次比赛才能决出前三名?(在第6章给出了这一问题的解答。(难度系数3颗星))
答:参考了计算之魂第6章给出了的解答。
- 先进行五组比赛,决出各组名次。并令各组第一分别为A1、B1、C1、D1、E1。
A组 | B组 | C组 | D组 | E组 |
---|---|---|---|---|
A1 | B1 | C1 | D1 | E1 |
A2 | B2 | C2 | D2 | E2 |
A3 | B3 | C3 | D3 | E3 |
A4 | B4 | C4 | D4 | E4 |
A5 | B5 | C5 | D5 | E5 |
- 第六组比赛:各组第一 A1、B1、C1、D1、E1 分别进行一次比赛。假设名次为 A1、B1、C1、D1、E1 。则整体第一名为A1。
- 第七组比赛:如下表,整体第二名必出现在 A2、B1之间;整体第三名必出现在 A2、B1、 A3、B2、C1 之间。
A2、B1、 A3、B2、C1 之间进行第七组比赛,第一名为整体第二名,第二名为整体第三名。
A组 | B组 | C组 | D组 | E组 |
---|---|---|---|---|
B1 | C1 | D1 | E1 | |
A2 | B2 | C2 | D2 | E2 |
A3 | B3 | C3 | D3 | E3 |
A4 | B4 | C4 | D4 | E4 |
A5 | B5 | C5 | D5 | E5 |
思考题1.4.2 区间排序
如果有N个区间[l1,r1],[l2,r2],…,[lN,rN],只要满足下面的条件我们就说这些区间是有序的:存在xi∈[li,ri],其中i=1,2,…,N。
比如,[1, 4]、[2, 3]和[1.5, 2.5]是有序的,因为我们可以从这三个区间中选择1.1、2.1和2.2三个数。同时[2, 3]、[1, 4]和[1.5, 2.5]也是有序的,因为我们可以选择2.1、2.2和2.4。但是[1, 2]、[2.7, 3.5]和[1.5, 2.5]不是有序的。
对于任意一组区间,如何将它们进行排序?(难度系数3颗星)
答:该题解答参考了博客《计算之魂》思考题1.4 - Q2,内容如下:
- 按照右端点排序;
- 将右端点最大的区间作为头区间,讨论剩余 n -1 个区间的情况;
- 对剩余的 n - 1 个区间按照步骤二重复,直至仅剩 4 个区间为止(也就是右端点最小的四个区间);
- 如果第 4 个区间不能和第 3 个区间交换,则按照上图可以得到前三个区间的情况,并递归地讨论第 4 个区间与第 5 个区间是否能交换,直至递归到最后一个区间,算法终止;
- 如果能交换,则将第 3 个区间作为最大的区间,并可以得到 N1、N2、N4(按照右端点的相对大小排序)这三个区间的情况,并归位;
- 讨论第 4 个区间能否与第 2 个区间交换,不能的话则返回步骤 4;
- 能的话则返回步骤 5,直至讨论到第 4 个区间与第 1 个区间是否能交换,并返回步骤4。