原文地址:
http://hi.baidu.com/void_while/blog/item/e5ed992237814541ad34defa.html
一、原因是 对原始算法每次合并都要动态开辟O(n)的空间且全部拷贝的做法耿耿于怀,部分灵感来自编程珠玑第2章的手摇算法。
二、代码
01/**
02
* 算法: 交换二对象
03
**/
04
template
<
typename
T
>
05
void
t_swap
(
T
&
v1
,
T
&
v2
)
06
{
07
T
t
=
v1
;
v1
=
v2
;
v2
=
t
;
08
}
09
10
/**
11
* 算法: 反转序列
12
**/
13
template
<
typename
T
>
14
void
t_reverse
(
T
*
v
,
size_t
size
)
15
{
16
size_t
s
=
0
,
e
=
size
-
1
;
17
while
(
s
<
e
&&
s
<
size
&&
e
>
0
)
t_swap
(
v
[
s
++
],
v
[
e
--
]
);
18
}
19
20
/**
21
* 算法:
手摇算法,从指定位置旋转序列(见编程珠玑第二章)
22
**/
23
template
<
typename
T
>
24
void
t_exchange
(
T
*
v
,
size_t
size
,
size_t
n
)
25
{
26
t_reverse
(
v
,
n
);
t_reverse
(
v
+
n
,
size
-
n
);
t_reverse
(
v
,
size
);
27
}
28
29
/**
30
* 算法: 合并二已排序的连续序列
31
**/
32
template
<
typename
T
>
33
void
t_merge
(
T
&
v
,
size_t
size
,
size_t
pos
)
34
{
35
size_t
fir
=
0
,
sec
=
pos
;
36
while
(
fir
<
sec
&&
sec
<
size
)
37
{
38
while
(
fir
<
sec
&&
v
[
fir
]
<=
v
[
sec
]
)
fir
++
;
39
size_t
maxMove
=
0
;
40
while
(
sec
<
size
&&
v
[
fir
]
>
v
[
sec
]
)
maxMove
++
,
sec
++
;
41
t_exchange
(
&
v
[
fir
],
sec
-
fir
,
sec
-
fir
-
maxMove
);
42
fir
+=
maxMove
;
43
}
44
}
45
46
/**
47
* 算法: 归并排序
48
**/
49
template
<
typename
T
>
50
void
t_merge_sort
(
T
*
v
,
size_t
size
)
51
{
52
if
(
size
<=
1
)
return
;
53
t_merge_sort
(
v
,
size
/
2
);
54
t_merge_sort
(
v
+
size
/
2
,
size
-
size
/
2
);
55
t_merge
(
v
,
size
,
size
/
2
);
56
}
三、分析
原理:合并时,将后半部最长可插入前半部的序列用手摇算法交换过去。
效率:空间开销为0,时间效率还没与原始的归并排序做比较。预计在序列大致有序情况下会比原始算法效率有较大提高。
最好情况:序列本身已排好序,只需做(n*logn)/2次比较,没有移动。
最坏情况:每层合并都是诸如(2 4 6 8 10)(1 3 5 7 9)这样的交叉逆序子序列,需做(n/2)(n/2+1)logn次移动,不过这种情况几乎不可能出现 。
补充说明:
其实前面采用"旋转"算法将元素前移不是必须的, 可以将所要移动的元素之前的部分后移, 再将元素插入到合适的位置.比如前面说的对于序列1, 3,
5, 7, 2, 4, 6, 8, 第一步要将元素2前移至3之前, 可以将3,5,7后移, 然后将2插入到合适的位置.
但是这样有一个问题,
如果要移动的元素多了, 那么就需要更多的临时空间保存要前移的元素, 这样对空间就不是O(1)的了.而旋转算法可以做到O(1)的空间达到要求.