2-1
在归并排序中对小数组采用插入排序
a.
对于每个长度为
k
k
k的子表,其最坏情况的时间为
Θ
(
k
2
)
\Theta(k^2)
Θ(k2)
总共有
n
k
\frac{n}{k}
kn个表,那么其用时为
Θ
(
n
k
∗
k
2
)
=
Θ
(
n
k
)
\Theta(\frac{n}{k}*k^2)=\Theta(nk)
Θ(kn∗k2)=Θ(nk)
b.
合并n个节点操作的时间为
Θ
(
n
)
\Theta(n)
Θ(n)。
将
n
k
\frac{n}{k}
kn个表示为二叉树的叶子节点,那么在最坏情况下,这么多叶子节点恰为2的幂,因为这样,合并操作的次数最多。
令
a
=
n
k
a=\frac{n}{k}
a=kn
那么,最底层有a个节点,且为2的幂,树高为
l
g
a
+
1
lga+1
lga+1。
设ck为求解规模为k的问题所需时间以及在分解步骤与合并步骤处理每个数组元素所需的时间。
最后一层,所需时间为:
a
c
k
ack
ack
考虑树的倒数第二层,所需时间为:
a
2
∗
2
k
=
a
c
k
\frac{a}{2}*2k=ack
2a∗2k=ack,其中k为合并k长度的表的合并操作,
a
2
\frac{a}{2}
2a为合并操作的次数。
考虑树的倒数第三层,所需时间为:
a
4
∗
4
k
=
a
c
k
\frac{a}{4}*4k=ack
4a∗4k=ack,其中2k为合并2k长度的表的合并操作,
a
4
\frac{a}{4}
4a为合并操作的次数。
由此得到每一层需要的时间为
a
c
k
ack
ack,总共有
l
g
a
+
1
lga+1
lga+1层,因此执行总时间为:
a
c
k
(
l
g
a
+
1
)
=
n
c
l
g
n
k
+
n
c
ack(lga+1)=nclg\frac{n}{k}+nc
ack(lga+1)=nclgkn+nc,即运行时间为
Θ
(
n
l
g
n
k
)
\Theta(nlg\frac{n}{k})
Θ(nlgkn)
c.
k
∈
[
1
,
n
]
k\in[1,n]
k∈[1,n],当k=n时,算法演变为插入排序,当k=1算法演化为归并排序。
在最坏条件下,k元素的插入排序的时间可以表示为
a
k
2
+
b
k
+
c
ak^2+bk+c
ak2+bk+c,其中a,b,c为常数因子(这里见书P15)。
由b知,最坏情况下的归并过程需要总时间为:
d
n
(
l
g
n
k
+
1
)
dn(lg\frac{n}{k}+1)
dn(lgkn+1),d为常数因子。
此算法所用总时间为,
T
1
(
n
)
=
a
k
2
+
b
k
+
c
+
d
n
(
l
g
n
k
+
1
)
T_1(n)=ak^2+bk+c+dn(lg\frac{n}{k}+1)
T1(n)=ak2+bk+c+dn(lgkn+1)
只考虑归并排序时,所需总时间为:
T
2
(
n
)
=
d
n
(
l
g
n
+
1
)
T_2(n)=dn(lgn+1)
T2(n)=dn(lgn+1),因此要使两算法有相同的时间,则需要
T
1
(
n
)
=
T
2
(
n
)
T_1(n)=T_2(n)
T1(n)=T2(n),当k为方程
T
1
(
n
)
=
T
2
(
n
)
T_1(n)=T_2(n)
T1(n)=T2(n)的解时,两算法具有相同的运行时间。
解此方程:
根据最坏条件下有:
n
=
e
k
n=ek
n=ek,且
e
=
2
f
e=2^f
e=2f,其中e和f均为常数因子
T
1
(
k
)
=
a
k
2
+
b
k
+
c
+
d
∗
k
∗
2
f
(
f
+
1
)
=
a
k
2
+
(
b
+
d
∗
2
f
(
f
+
1
)
)
k
+
c
T_1(k)=ak^2+bk+c+d*k*2^f(f+1)=ak^2+(b+d*2^f(f+1))k+c
T1(k)=ak2+bk+c+d∗k∗2f(f+1)=ak2+(b+d∗2f(f+1))k+c
T
2
(
k
)
=
d
∗
k
∗
2
f
(
l
g
k
+
f
+
1
)
T_2(k)=d*k*2^f(lgk+f+1)
T2(k)=d∗k∗2f(lgk+f+1)
解
T
1
(
k
)
=
T
2
(
k
)
T_1(k)=T_2(k)
T1(k)=T2(k)方程可以得到k值。
d.
在确定常数因子之后,根据上述公式计算
2-2
a.
不需证明其他。
b.
循环不变式:数组
A
[
j
.
.
.
n
]
A[j...n]
A[j...n]其中的
A
[
j
]
A[j]
A[j]必为其中最小的值;
初始:
当
j
=
A
.
l
e
n
g
t
h
j=A.length
j=A.length时,就一个元素,因此成立。
保持:
若
j
=
k
j=k
j=k时,循环不变式为真,
那么当
j
=
k
−
1
j=k-1
j=k−1时,
A
[
k
.
.
.
n
]
A[k...n]
A[k...n]中
A
[
k
]
A[k]
A[k]最小,若
A
[
k
−
1
]
A[k-1]
A[k−1]大于
A
[
k
]
A[k]
A[k],那么互换,反之则不变,因此,结果均是循环不变式为真。
终止:
导致for循环终止的条件是,
j
<
i
+
1
=
i
j<i+1=i
j<i+1=i,且
A
[
j
]
A[j]
A[j]为
A
[
j
.
.
.
n
]
A[j...n]
A[j...n]中最小值。
c.
循环不变式:数组
A
[
i
.
.
.
n
]
A[i...n]
A[i...n]其中的
A
[
i
]
A[i]
A[i]必为其中最小值;
初始:
当
i
=
1
i=1
i=1时,内部循环表达式为真,即,A[1]为A[1…n]中的最小值。
保持:
当
i
=
k
i=k
i=k时,循环表达式为真,
那么当
i
=
k
+
1
i=k+1
i=k+1时,因为
i
=
k
i=k
i=k时,内部循环不变式为真,因此,有
A
[
k
.
.
.
n
]
A[k...n]
A[k...n]中,
A
[
k
]
A[k]
A[k]为其中的最小值,有
A
[
k
+
1...
n
]
A[k+1...n]
A[k+1...n]中,
A
[
k
+
1
]
A[k+1]
A[k+1]为其中的最小值,且必然存在
A
[
k
]
<
A
[
k
+
1
]
A[k]<A[k+1]
A[k]<A[k+1]
终止:
当
i
>
A
.
l
e
n
g
t
h
−
1
=
A
.
l
e
n
g
t
h
i>A.length-1=A.length
i>A.length−1=A.length时,终止,此时由于对于数组
A
[
n
]
A[n]
A[n]恒有
A
[
k
]
<
[
k
+
1
]
A[k]<[k+1]
A[k]<[k+1],因此可以判断数组已经排序。
d.
冒泡排序必然会执行语句
c
1
,
c
2
,
c
3
c_1,c_2,c_3
c1,c2,c3,最坏情况下,必然执行
c
1
,
c
2
,
c
3
,
c
4
c_1,c_2,c_3,c_4
c1,c2,c3,c4,最坏情况下的运行时间为
Θ
(
n
2
)
\Theta(n^2)
Θ(n2),最好情况下运行时间也是
Θ
(
n
2
)
\Theta(n^2)
Θ(n2),无论序列的状态是什么,其运行时间都为
Θ
(
n
2
)
\Theta(n^2)
Θ(n2)
插入排序最坏情况也是
Θ
(
n
2
)
\Theta(n^2)
Θ(n2),最好情况下是
Θ
(
n
)
\Theta(n)
Θ(n),而插入排序时序列越“整齐”,运行时间越短。因此,插入排序性能优于冒泡排序。
2-3
a、
第一条语句是常数时间,第二条语句执行
n
+
2
n+2
n+2次,第三条语句执行
n
+
1
n+1
n+1次,因此,运行时间为
Θ
(
n
)
\Theta(n)
Θ(n)
b、
伪代码:
y=0
for i=0 to n
for j=0 to i:
x = x * x
y = ai + x + y
此算法的运行时间为
Θ
(
n
2
)
\Theta(n^2)
Θ(n2),与霍纳规则相比,其性能较差。
c、
初始:
当
i
=
n
i=n
i=n时,
y
=
0
y=0
y=0,成立
保持:
当
i
=
j
i=j
i=j时:
y
=
a
j
+
x
∑
k
=
0
n
−
(
j
+
1
)
a
k
+
j
+
1
x
k
=
a
j
+
∑
k
=
0
n
−
(
j
+
1
)
a
k
+
j
+
1
x
k
+
1
=
∑
k
=
0
n
−
j
a
k
+
j
x
k
\begin{aligned}y &=a_j+x\sum^{n-(j+1)}_{k=0}{a_{k+j+1}x^k}\\ &=a_j+\sum^{n-(j+1)}_{k=0}{a_{k+j+1}x^{k+1}}\\ &=\sum^{n-j}_{k=0}{a_{k+j}x^{k}} \end{aligned}
y=aj+xk=0∑n−(j+1)ak+j+1xk=aj+k=0∑n−(j+1)ak+j+1xk+1=k=0∑n−jak+jxk
即等于当
j
=
j
−
1
j=j-1
j=j−1的时原式的表示。
终止:
当i=0时,循环不变时终止,且:
y
=
a
0
+
x
∑
k
=
0
n
−
1
a
k
+
1
x
k
=
a
0
+
∑
k
=
0
n
−
1
a
k
+
1
x
k
+
1
=
∑
k
=
0
n
a
k
x
k
\begin{aligned}y &=a_0+x\sum^{n-1}_{k=0}{a_{k+1}x^k}\\ &=a_0+\sum^{n-1}_{k=0}{a_{k+1}x^{k+1}}\\ &=\sum^{n}_{k=0}{a_{k}x^{k}} \end{aligned}
y=a0+xk=0∑n−1ak+1xk=a0+k=0∑n−1ak+1xk+1=k=0∑nakxk
d、
终止时有
y
=
∑
k
=
0
n
a
k
x
k
y=\sum^{n}_{k=0}{a_{k}x^{k}}
y=∑k=0nakxk,即多项式的值。
2-4
a、
(
8
,
6
)
,
(
2
,
1
)
,
(
3
,
1
)
,
(
8
,
1
)
,
(
6
,
1
)
(8,6),(2,1),(3,1),(8,1),(6,1)
(8,6),(2,1),(3,1),(8,1),(6,1)
b、
当{1,2,…,n}逆序时,有最多逆序对,总共有:
1
+
2
+
3
+
.
.
.
+
n
−
1
=
n
(
n
−
1
)
2
1+2+3+...+n-1=\frac{n(n-1)}{2}
1+2+3+...+n−1=2n(n−1)对。
c、
考虑书中INSERTSORT运行时间,其中while循环中的数组元素位移操作与逆序对有直接关系,因此,逆序对越多,运行时间越长。
插入排序的运行时间主要是由于数组元素的移动造成的不稳定性,每有一个逆序对,就执行一次交换操作,因此,可以运行时间跟逆序对的个数有直接关系;
当逆序对数小于n时,由于要遍历整个数组,因此运行时间为
Θ
(
n
)
\Theta(n)
Θ(n);
当逆序对数大于n时,运行时间为
Θ
(
逆
序
对
数
)
\Theta(逆序对数)
Θ(逆序对数);
d、
算法:
修改归并排序中merge操作可以实现。
在merge操作中,若左边的数组大于右边则逆序对数量+1。这样就可以在排序完成之后得到逆序对数量,并且,只是在判断语句里加了常数性语句。因此,运行时间仍为
Θ
(
n
l
g
n
)
\Theta(nlgn)
Θ(nlgn)