数学建模社区-数学中国
标题:
【基于C的排序算法】归并排序
[打印本页]
作者:
杨利霞
时间:
2022-9-14 16:22
标题:
【基于C的排序算法】归并排序
【基于C的排序算法】归并排序
" ] Q: K) \2 ~. x5 \8 |, q
- j8 S5 Q& \- M& O" ?
前言
) s) a- Y" Y( _) g8 y7 Z" I) n
本文基于C语言来分享一波笔者对于排序算法的归并排序的学习心得与经验,由于水平有限,纰漏难免,欢迎指正交流。
5 \$ e& T8 Y) ?6 y
/ K0 o+ _2 N' q) n: k8 G6 e/ x. z
归并排序
/ Y2 q" \ h' y; C* t. B
基本思想
2 Y1 W% k& ?7 F6 c! K9 S! \+ L
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
+ A- w4 [/ B8 n3 [# _
7 T. ^! f1 m) f1 }* i
1 n3 K: o4 Z# @8 x% a
; J" l6 A1 T' V9 ~4 O
合并的思想其实和有道题目的思想如出一辙:
+ v! }6 w. y! [; y8 n0 Q
J: o. x4 i1 o T D, f8 @0 @! P
* E$ U6 W1 ^. b1 l1 E) ]
. Y2 Z; C$ T$ l5 w0 O" ?
我们考虑新开一个数组来放入排序好的值,要不改变顺序的话就要用尾插,让nums1和nums2数组元素的较小值尾插到新数组中,两个数组总会有一个先插完,另一个数组就把剩下的全部尾插接在新数组后面。
: h) C- R& W2 a" c# P# A" T
- j5 e$ ]. b: Q P6 f, W1 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gsgj7Cmx-1662793985599)(https://typora-picture-1313051246.cos.ap-beijing.myqcloud.com/归并原理.gif)]
$ `- I2 ^+ m: J7 y) S7 m
9 W8 G% `9 j) ?
int* merge(int* nums1, int m, int* nums2, int n)
8 J/ s* L& E( U9 ?1 _, P
{
3 E7 r% m8 t& }* A
int* arr = (int*)malloc((m + n));
1 k! i4 L/ q" _! g: ]
if(arr == NULL)
6 u% l, H7 U" w) }8 J
{
7 D1 S' P& X# n7 Z, o
perror("malloc fail");
5 t1 | _9 }; `4 l" i# z9 O
return;
& X4 l! r( }2 K# ~# ]
}
8 V G5 a U8 c5 Z
0 Q- g7 S9 X& N9 A
int p1 = 0;
, }1 t% g( N5 q( D3 n7 n0 C* u
int p2 = 0;
t1 B3 @- {( z8 o: S2 D/ s! F
int cnt = 0;
0 u# T3 z( h0 L. L( z" Z0 G
while(p1 < m && p2 < n)
* F! e( w- y9 M; N
{
( V1 u G7 h* B; [5 i
if(nums1[p1] < nums2[p2])
: Z- ~/ }0 G9 n! I
{
8 _+ ~ h9 P8 u- K; ?4 C+ G8 R
arr[cnt++] = nums1[p1++];
, c' O0 n6 Z. _
}
6 {' X: @& |6 o; f5 g
else
" w+ ~, E# A1 E! F# V( [
{
! y% `) _- ?( R- }4 i3 \
arr[cnt++] = nums2[p2++];
0 G" z2 v+ o, C8 V0 ~7 ]
}
' z, I9 x5 |3 ^( ~
}
8 s1 ~6 m8 I5 R+ j
while(p1 < m)
( x$ b k, \6 q. Y7 n+ l
arr[cnt++] = nums1[p1++];
% t) b: ^* p" Z
* Y+ c$ l3 [" {; D M
while(p2 < n)
! x+ L1 o/ ?3 L1 a% k7 R9 o5 X" ?
arr[cnt++] = nums2[p2++];
7 q: j% T5 @+ e) U$ k+ q7 H
2 m1 m5 z6 k/ W! C3 a
return arr;
5 S* X% e, z) a, S6 y
}
' t4 R; @4 O9 z- j' z r. S( e# u
( q+ u6 ^9 C1 R8 ?. T0 `$ ]# z
1
% Y8 K f e: s- c/ i+ |3 s
2
# @3 ~* T. O. r+ A( f0 v. s
3
* ?/ T; Z7 N4 s' E% h8 X0 x6 b+ E& O
4
5 K1 a8 I5 U' ^4 Y; @
5
: i# c: h( _: e
6
# h {) @. J2 _5 b1 A- n
7
, |6 W7 N" ^9 Z% T$ X: k6 }
8
, J! H0 U: }; [" V" J4 Y7 A& [& w( d
9
0 e/ b a# \+ W2 Q# \* K! n- b
10
: W' e# m' {4 h9 @" c/ f
11
0 H9 n% X3 o/ z- L
12
$ Z' X1 r9 Z+ N3 P
13
8 @' H! r9 R$ P7 N$ M
14
( \( O) e3 ?9 P( ^4 O7 p2 \; V
15
1 _$ K6 l, F6 _3 e+ |* M5 ^
16
/ V0 s5 @+ s& @9 x
17
# i% R+ j, x' p4 E: [) t
18
% X) _7 Q8 w% e* U: s
19
) ?( W5 b5 }( ^/ f. v7 Z* ^8 y9 C% K
20
8 E8 G# J: w- r3 s
21
# x: ?! J) x% _
22
! x% [7 B5 X8 c, S
23
8 ~( w) h- L; ?/ j1 g
24
" q# E. }- m$ A* a3 j6 t+ k! G6 w
25
8 e8 z1 @7 S- \2 E! |
26
5 d( \* d9 {2 M" f, U
27
) R# v- n- c `2 m8 ~/ i6 q/ y
28
0 Z( o9 y D" Z: F3 d
29
) l( U. E( U) d6 a6 u# s/ J
30
3 K6 d5 D2 H+ Q1 e7 p9 X& ~, o
31
" @" W- Z. u& t9 l9 d. z4 K# W
所谓的合并就是利用这样尾插的思路,我们就想到要把原数组分解成两个有序子序列来合并,那么如何将原数组分解成有序子序列呢?容易想到用递归,其实非递归(迭代)也能实现,我们接下来具体来看看实现方法。
% R9 }' L @' s% x, T
4 L+ M" f( d/ x& ~! Z, [7 ]
递归实现
$ V5 ?; |: H8 W
通过二分分割出两个子序列,然后进行递归,先左后右,不断分割直到子序列仅有一个元素时子序列一定有序,这时候就可以往回退了,等到左右子序列都退回后就可以归并了,不过不能直接归并到原数组,因为会覆盖而丢失值,不妨归并到另一个辅助数组,归并后再拷贝回原数组,思想就是前面讲的合并的思想。
/ C8 c) g b6 s# K
/ k% j: y9 l% _( ~3 V
$ U' h" H) s/ Z# |$ |' ?2 h# G
9 r3 e' M" ~2 E# B0 |
- ^) p+ Q9 @& G0 q' G: O8 j
) v4 m( A1 ~6 W3 o* o6 _9 v4 D
void _MergeSort(int* arr, int* tmp, int left, int right)
& c. E" C9 y: A* h
{
* z% b9 ^1 N6 m# ^
assert(arr);
2 h$ G* T0 h2 @ m. A0 W& _
4 t \3 G7 Z8 o$ @6 J7 ~5 _
if (left >= right)//递归结束条件不要漏了
4 E" A# {9 @2 W4 o2 f5 Z% _. F, k# H& A
return;
% @( |# @4 P8 @0 `9 f% x1 u9 G
7 I; F$ X# _& D
int mid = (right - left) / 2 + left;
' C' j# K* y: z0 M9 s
2 M$ o1 {$ L7 S* b) z5 I; w
//划分左右子区间[left, mid]和[mid + 1, right]
( g S0 Z3 S8 ?
_MergeSort(arr, tmp, left, mid);
2 K F: i( A4 S' U8 R3 z }1 D+ B
_MergeSort(arr, tmp, mid + 1, right);
9 }0 l2 B7 [' _* z5 Q" W+ m
0 O* z- c+ O) H3 I/ t6 y. S
//归并
* Z4 J( j- k. X" m: ]$ y. R
int begin1 = left, end1 = mid;
+ c( O( t. Y$ i$ y, y& K) @
int begin2 = mid + 1, end2 = right;
; R2 a4 p' O7 Y P
int i = left;
2 B! V, \9 m3 d* s
while (begin1 <= end1 && begin2 <= end2)
+ n. O& }/ }) ~1 o- D
{
( P7 C8 k* b5 ~9 u( p1 D
if (arr[begin1] < arr[begin2])
2 h' V; B& @) j8 X1 v
tmp[i++] = arr[begin1++];
$ W U& Z9 y& g; Z- z5 a5 J9 ~
else
" f+ {5 I+ z5 ~+ Z7 D! q! c
tmp[i++] = arr[begin2++];
6 |! b2 A. s, @" B
}
" o/ {/ V$ @! E% x# a/ W& K1 m6 C$ [( J
5 r* W6 I$ A$ L/ D4 T7 Z4 N% `( ?
while (begin1 <= end1)
! r9 e" M! Q4 i& [3 ]0 r
tmp[i++] = arr[begin1++];
% b6 b! y: x8 x* }2 N# u7 [
while (begin2 <= end2)
, i+ D7 q- K7 ~6 ~
tmp[i++] = arr[begin2++];
7 T3 z% Y: l6 B& s# i; l
( A. u8 l& U7 \1 s& k
//拷贝回原数组——归并哪部分就拷贝哪部分回去
- ~1 Y8 g2 S3 D0 u. {, K
//而不是拷贝整个数组回去
H! W* d6 B6 i \+ G
memcpy(arr + left, tmp + left, sizeof(int) * (right - left + 1));
9 q1 t7 f& D: i4 R c# B) ?. @
}
. g3 }3 N2 A$ t: g* l- E
$ P+ S2 ]# _4 R
void MergeSort(int* arr, int left, int right)
. F, ]; W2 G% ]3 B4 L; I/ u, g
{
1 s( o5 s! h9 e5 @
assert(arr);
. E1 x* P1 r/ l& o% J' c( s
t0 r5 R' [3 ^
int* tmp = (int*)malloc((right - left + 1) * sizeof(int));
. G4 H2 |! h" l8 D. E3 `
if (tmp == NULL)
5 i) |; t) _8 i+ m" {
{
- J, y5 L1 b+ w8 e; n, M0 m
perror("malloc fail");
9 W4 j Y9 c! h- G% ?2 _
return;
1 \9 O0 f k; o5 g7 |6 W( Q
}
. Y/ \' Z3 Y$ C
& N a4 d8 Y: c! O4 @# \; Y- H
_MergeSort(arr, tmp, left, right);
: h0 v7 {! ]+ W6 J5 y
8 {. ?# |1 F2 `; }' J) d( j
free(tmp);
* l6 r# o7 Y( l9 D* R+ ~
tmp = NULL;
0 s' t$ `3 I9 O3 m# A- {
}
$ n- A, s! ]/ n% _+ S
6 @ Z. w" s6 X6 G; K6 }
1
, X8 z# ~7 e" E6 I3 |
2
9 G/ a: M0 R. Q/ {
3
9 I7 u. t: q T6 G, D/ E/ l. F
4
+ q2 \; J$ S8 J! g2 I
5
- \/ n* M' n+ k
6
j9 k" A6 N! l8 t4 `1 q
7
! G) h* E# h, z( L! Z; b
8
" t' @8 n+ L6 S$ ~
9
7 u7 C/ X0 x' E; W- o
10
' N2 U* {4 R7 x$ t5 y, h
11
2 K: ]+ Q1 t: ^( P' r h' X% k' i1 ~
12
7 w+ b% h+ j0 U3 j
13
- F k& G( A2 P% _3 _
14
2 y! o- }) Q: M/ n% M! y9 w
15
' Z9 _ V' P% r2 E) P
16
( k# G! [0 p, w7 Y5 R; `
17
. U! q k2 ~4 B7 S5 h O
18
( O1 Z5 @% H, G v) ~
19
9 z* ]3 {$ H4 q" Q
20
8 [' T2 a) V$ W$ Y
21
* K, M; J* c8 |8 I# y
22
* K7 K* t* i- g9 x5 V8 m
23
( K/ H; g) ?$ A" b& g
24
% A# X0 H! P+ | v' \4 h1 Y! Z
25
8 o7 Q3 Q* I2 T- l c* v
26
. ^8 m# w+ h8 J1 V, o
27
) d6 ]( C% x) b) ?/ g% A) ?
28
8 r5 e( o; L3 s' v. m
29
" q' U2 o9 I! C K3 o `+ `# j( f
30
5 y# s) c/ B0 M. N
31
% |* Y& I- s' R2 x& {: ~
32
3 [5 k9 w! B9 t) N
33
1 b7 [) j9 B; G) V
34
3 {4 ~% F5 K8 d: K' C% h3 x
35
9 Z3 l5 } A3 t
36
4 S$ [6 e8 e* x+ v8 `4 W; P- R. n! G" z
37
8 a' ]7 s- q4 y" } d/ s
38
+ Q! d! T: M- Z6 u# k* S
39
' U ]! `, Q' A j) m' X! V
40
1 O# U. F2 k4 W: J
41
$ u4 ]9 b0 ^( \9 h( S4 c
42
/ |! T3 Y4 A% { \/ i" ~3 [
43
, U) j. i" j; X
44
6 w( y3 ^8 e$ ^8 Q2 z
45
5 s& v H. J! Y; J( Y2 G4 ]+ O
46
! j1 ?, F4 l* I! v" F3 |) R3 ]
47
7 O; o; n* P/ c$ }) z; I f
48
% L) S( `4 \- K8 l$ v* n
49
! [' y9 x: ?: N
50
2 f! M- _! p8 c" j! h1 i
51
7 j! _9 E5 A: G) j. P+ y
非递归实现
( i! a% [, G# H1 y) w! Z
直接在原数组基础上归并,设gap是子区间元素个数,从gap = 1开始,因为仅有一个元素的子区间一定有序。为了方便,我们把gap=1叫做第一层,以此类推。
4 n' v! {9 E' Z @
. k! n' e6 |# z- P$ p4 Z( q# w) {
( K- s1 b" u8 e0 }
4 u9 `: ]0 u# ~( X
不同的gap值代表所在层数不同,每一层都是从左到右两组为一对地取对配对归并,i就是每对起始位置,之所以更新i的时候要i += 2 * gap是因为每队两组、每组gap个元素,所以要让i跑到下一对的起始位置的话不就要跳过一整对的空间嘛。
: K8 ?# q! g0 C/ @
, p8 B! ]9 _* e2 ~9 m
还要注意区间的取值,每个区间就是一组,就有gap个元素。
( U. d' `2 m6 v& k; I, S
7 s3 S" X# {3 j, E
整体拷贝遇到越界就会比较难搞,所以我们这里用部分拷贝的思路,每次归并后直接拷贝,要注意一下指针偏移量不是begin1而是i,因为begin1已经在归并过程中被改变了。
% X) d& h0 X: I* u
$ z0 [& m1 l- q* _+ s$ b
代码实现
0 e7 G7 `4 d- e; U8 X( L/ _
2 N9 _/ G) d0 ~; y) I
void MergeSortNonR(int* arr, int sz)
( }6 h. z4 u7 e5 ^
{
' O$ h& E) R; c, u. C* ^) R5 K
assert(arr);
9 B e* `1 Q! e
/ n i3 N) b/ p
int* tmp = (int*)malloc(sz * sizeof(int));
! C' E& a( V \3 J( d7 }: Z* |. o
if (tmp == NULL)
2 ^' q; z, q% D( E# J- x. Y7 P6 H
{
, e) @8 b U! p3 }
perror("malloc fail");
0 u1 ~( f/ ]7 s4 Z
return;
0 a% |9 ]. f' h& R
}
4 ^6 X R# K; h( g4 n
( F1 n; p5 _" g q
int gap = 1;
# t) U- L. c9 ^" Y& f% ?
while (gap < sz)
, }4 {. w: U! x3 ^ O( ]8 A
{
* s; z( {3 |6 \$ z% Y5 {1 V, [
for (int i = 0; i < sz; i += 2 * gap)
E6 c7 |4 n& S/ q% O
{
I& s+ }8 E8 u; k! F" T' n
int begin1 = i, end1 = begin1 + gap - 1;
: U& M9 n1 I% \! t; n
int begin2 = end1 + 1, end2 = begin2 + gap - 1;
# M% H: F2 g! [$ |) a
int j = begin1;
# P0 v h8 b* @3 G3 w3 e
$ A# z# E3 i4 e6 u' ]
//归并
7 r% d4 n) L1 T0 q
while (begin1 <= end1 && begin2 <= end2)
3 k( b' R# Q5 W0 I' `' f- @
{
- \" ^8 G, U: s% C1 Y$ v$ j
if (arr[begin1] < arr[begin2])
7 a3 h! G8 y0 a: r" K2 o
tmp[j++] = arr[begin1++];
/ c$ x5 Q0 T% \9 F8 I1 r
else
3 u" M& ~* I+ i
tmp[j++] = arr[begin2++];
2 b: e7 j+ D( V4 J* o; h+ Y$ Z
}
9 Y! d5 h6 a5 `5 G! }( I1 u7 n% ~: M
" v5 V! V# [+ w6 f& @% }. X' B
while (begin1 <= end1)
2 @' j% s9 J% w( o
tmp[j++] = arr[begin1++];
' e+ }* v$ q9 u/ |
while (begin2 <= end2)
# S$ k. v$ A! H8 v/ E/ T
tmp[j++] = arr[begin2++];
) j" s1 [$ x A( ?) `. G- z7 t; e
) C" J$ K8 m6 t4 C1 o7 D& J/ {- ]
//拷贝回原数组——归并哪部分就拷贝哪部分回去
- D# l0 |' t6 W, A. L- W% A1 [
memcpy(arr + i, tmp + i, sizeof(int) * (end2 - i + 1));
$ O3 p' F. _0 `1 [
}
: u2 ^) @; [/ O( @" L
gap *= 2;
3 u1 Q5 s+ Z2 z ^: o$ T, g* d
}
, I! ]! T) h# E: Y b/ F+ Q
3 E1 f$ {7 \0 a
}
, d2 Y- F4 K5 k+ J! B
* E$ }3 `4 q* K6 H9 O8 u. M% d6 E
1
9 [: \8 J3 S2 H9 M9 F& `
2
9 X$ }/ Y7 n$ Q a7 O M
3
+ F* t5 d7 c* I6 ?: q, @ o! X* X
4
. Z8 s. I0 T* J+ u6 F2 f/ W/ S2 C1 ~
5
% K0 s# N- F( ~1 B: l, m M; q
6
8 t/ a& k( ?% H' p
7
9 b" b% Q/ j% g/ A5 P
8
" [" m/ ]& x" Y6 `- B' y/ d
9
0 x5 D. @) d4 N+ ^; K- l" z- {
10
r [1 O% y0 i$ s3 ^' P2 s: X, e
11
* m; h g0 B5 J6 g+ ^2 ~ h7 t6 g4 K* I
12
1 {3 |! R, k( }8 A1 e k& H$ z$ M& d
13
% h! c& G5 R6 t4 c
14
: Z: ]( C% K' k. m
15
* ^1 g: x* Q% T( |, k8 Y7 X9 C
16
" L4 n5 f& G1 E3 K/ {4 k
17
+ b/ P7 z# @3 T0 h0 N+ N
18
+ p7 Y5 H$ p) r5 R
19
[' V! X; m; G; F$ I. F
20
4 j4 J9 M g7 V5 { v2 T
21
& c$ b6 R2 z) b/ C- o4 c0 @
22
5 b/ S; ~0 C8 K) M3 F+ F
23
, s3 u# [$ _2 w6 ^
24
" A5 [9 z" H$ W- ?6 c! o/ S3 b
25
& d u- z3 s& A5 k/ ^+ \3 n3 v& q" w
26
2 q; J. m0 T+ v, u
27
, ~. g$ }+ g* \/ k) [
28
( Z5 p" L1 k- C) P) Y3 F
29
# }) L! G$ v! w1 N$ c
30
B8 F) w2 ~: b- d: `" X
31
. c+ u; |( K; ?
32
4 ?5 L' c' n( L: ^5 o5 z
33
- |! f& @/ o o
34
% U B" U" n# H1 `% K; u0 b' |0 }& d
35
" G5 r/ n3 d# P& k6 {) x4 d: o
36
- B+ I; @ O- r/ C( Q8 u
37
0 m Y, @% i4 M& C3 {- Z* Q5 ?
38
0 P/ H' p! q7 x3 p& T/ P# q
39
9 X6 P0 ?1 B, s9 a, ]; X
40
. j) f5 K# n9 Z3 i! ?( b: n3 t
41
3 J. @+ s0 Z) i& D
边界问题
( B6 Z1 F* ^2 u1 b p/ K: d+ j
实际上还需考虑是否越界的问题,上面那段代码并没有考虑,所以还需一些改进。为什么会存在越界的可能呢?因为我们是以gap的整数倍去取区间来归并的,而区间个数不一定总能满足两两配对。
# Z _. B7 }( a5 k3 O0 H! r
0 f3 ~# k+ @7 z! W" K
举个例子,就把前面的那个数组后面加上个元素5,没有越界检测时出现的情况:
& l) h7 w0 j' O* H1 ^2 f
% y5 u6 Y' ^: B' N$ x
+ @8 A) R$ q3 l( d7 l7 B/ h( A' ^
& q `) n/ P: } y) _
由上图可知越界分为三类(这里将[begin1, end1]、[begin2, end2]分别作为第一和第二组)
; E2 k+ |5 ^. U# G7 p
" H( q+ {: }/ s1 {) d! w+ D* j
第一组越界(即end1越界)
2 ?+ b1 g) l9 o7 l+ {
1 f& h; c2 s3 _+ {( }2 v2 G& u
应对方法:这种情况一般介于第一层和最后一层之间,break跳出for循环,不让越界值被访问。
% Z$ x! f+ h$ O- A9 {
9 o6 v3 T7 N5 x7 |8 A( n) O
第二组全部越界(即begin2和end2越界)
g' F' D/ \$ ~& B1 F; \
k) f8 C; v. v4 g, q) i
应对方法:这种情况一般在第一层,break跳出for循环,不让越界值被访问。
7 y; t$ \5 h/ L4 L9 e
3 e2 g$ _. f: [, Z( W1 a9 m
第二组部分越界(即end2越界)
' m$ m: C; u/ r0 q
! h7 j Z% l9 p" O! t
应对方法:实际上这时候就到了最后一层了,把end2修正为sz - 1,不跳出for循环而继续归并。
1 G2 I# x$ F! U5 |
) r/ N( z3 c9 v2 N- Y9 m7 m+ }
其实第一种情况和第二种情况可以合并为一种情况,原因:
0 j- K. x* d; B
( d2 E- I2 u _- [+ R, K
end1越界时begin2和end2由于比end1大,它们两个肯定也越界了,也就是说发生第一组越界时满足end1、begin2和end2都越界,即包括了第二组越界的条件,这两种情况都满足判断条件begin2 >= sz && end2 >= sz,同时第一和第二种情况的操作都一样——break跳出for循环,所以可以合并为只判断第二组是否全部越界。
9 w/ Z, _7 m' ]% q" S
' ?# C, u6 N: J b, j. u" j% ^
拿两个数组试一下:
" U% Y1 ^3 c# H% P9 ]( [+ Y# l& u
2 \. n4 P+ e; L# H9 s9 u
6 ?2 S/ N$ t* E4 \
/ J4 f! g( U5 M/ N4 H. I( i
# d& h1 n* K+ ]
' n+ m" @2 ~% j: q
代码实现
3 G" N9 }% E3 b4 x5 ?
5 f+ G) c9 V. d
void MergeSortNonR(int* arr, int sz)
7 t) P$ t2 g% t. d1 T% H7 J
{
( y: [8 k* O6 X5 H! L) K
assert(arr);
) _5 V+ K9 n0 T; J
* C% B8 W$ n1 u# J4 H* z3 u
int* tmp = (int*)malloc(sz * sizeof(int));
" Q) e8 R. C" ^' }. l
if (tmp == NULL)
7 D4 m, r7 u0 @3 s
{
, Y) s I5 _2 i0 i, i; G
perror("malloc fail");
, @2 f+ O [( q% a ~8 v$ W
return;
7 [/ K) `: X( h: a! J" {; C' i
}
- ~$ Q9 J* l( M0 |- b
3 o% `+ o# i9 Y, `, |; ]
int gap = 1;
9 l }/ f& O$ n2 }
while (gap < sz)
, g. Z9 v8 [/ y( y
{
2 {6 ]3 ^. ~: Q7 X- h
for (int i = 0; i < sz; i += 2 * gap)
_9 f2 G2 t/ D# T
{
# ?+ l$ ?0 O" q! }
int begin1 = i, end1 = begin1 + gap - 1;
8 h2 X* E( F9 u% K, p* |7 _
int begin2 = end1 + 1, end2 = begin2 + gap - 1;
6 w" M# J0 `) h$ ?4 `! w4 A$ D( F
int j = begin1;
: p+ j9 G5 n- ]- g# j; e( g
//越界检测
0 P4 |8 H N6 Z
if (begin2 >= sz && end2 >= sz)
( Z, |% q( C" m( M( Q0 N+ ^+ T
break;
! D; |+ ~ ?. _
if (end2 >= sz)
$ M/ ~# f1 P, w
end2 = sz - 1;
) Z6 E/ I+ `' K
//归并
! _" S3 R! h2 h; U9 s9 A1 o) l$ t% ]
while (begin1 <= end1 && begin2 <= end2)
& J; K8 K2 C; c) J; ^3 N8 O
{
- b N2 a% ^) s, B% E, E. U
if (arr[begin1] < arr[begin2])
. h8 l: V! ~# n i. Z
tmp[j++] = arr[begin1++];
/ H0 z8 }# G- Q- L$ H+ t$ b4 L4 J7 F
else
5 r6 {5 J1 s# B# |- I
tmp[j++] = arr[begin2++];
" P1 O9 Z6 _8 T8 x* ~
}
c0 }9 k) `, G$ e3 H) Z- }, F
6 G- @! z0 @# l0 U: C
while (begin1 <= end1)
& Y3 {# L* N7 {
tmp[j++] = arr[begin1++];
) _3 y1 E6 |: `' y0 n" H8 L* H; o
while (begin2 <= end2)
`' L+ X J! m% l- q1 k0 ?+ R
tmp[j++] = arr[begin2++];
w5 f$ z. g" W# o9 s0 P, }
5 f$ R& |/ e; n4 Q# G/ j1 Y
//拷贝回原数组——归并哪部分就拷贝哪部分回去
/ e# ^% S) P+ N2 d2 L- z* t
memcpy(arr + i, tmp + i, sizeof(int) * (end2 - i + 1));
6 E9 g( s) o3 \+ X% T
}
$ u6 b$ B! Y+ d9 W
gap *= 2;
6 A7 t; g, y( X5 A
}
& B5 S& v' D/ w
: E3 }9 v( Z) ?" z9 A
}
1 h# X" |* t2 `6 S; v9 _
; N- h4 ^ B" N
1
7 W0 K# _& |- c' w, l* @# ?
2
- J4 U; d$ P/ Y0 r
3
" _3 n) |' `9 o: h2 @
4
3 u* {; V' M8 l8 ~9 P6 k: H& D
5
0 [0 J5 D$ U2 c5 j, R
6
$ P3 w+ F5 P4 y- ^
7
, C; M/ P$ x* e- J
8
; G, N- O" D/ B. T: N& T
9
: }2 V1 t" y% V' c: E
10
+ X5 i5 ~2 w7 B% i! |+ H* E0 k {
11
$ c* {' j" \+ C5 B# \
12
7 x z% p* |7 ^1 n2 h2 s
13
% \. Z+ R) X6 P$ H7 P0 ?
14
0 D9 X; F! Z( M& j) f
15
& [2 R* b; [+ ~( z' L0 C
16
& {% W& q1 |6 B2 q) m! X) i
17
) \6 T/ u5 g Z
18
: M3 U) ^1 E e8 |9 N8 u6 _
19
" f2 b2 I9 d5 P! \' W$ b% n7 a
20
% P, X4 j# w/ T) M5 Z, v
21
4 {1 A, @+ ?% C( {
22
- O( c; @0 b( R+ }6 X- d6 n0 J
23
# W' @5 C) G# ]" F7 g/ J
24
- W V( [. {1 B# O) Q) A: a
25
; w( T5 T6 E! C- c: z
26
$ O! E4 R0 H* `. X+ l
27
: f$ R, H$ v1 b& f- R
28
& C( G- c: l; o9 A- c. j
29
/ ^ l# e, n d, v$ u3 `2 c% t4 {
30
: a E' z$ k ~5 S+ _/ e2 A% i
31
! `; k6 V q# C: Y9 h4 w0 [, {
32
8 w4 ~7 y$ y9 E
33
! V ^& j5 E j& n
34
1 Z* M$ J& J2 g! \/ S4 z2 M, E, m
35
: B, N" P. b7 _, x8 j/ T, Z
36
8 ]5 V3 ]2 X; R+ A
37
+ Z1 p' k9 m, I! f; e
38
9 g3 ?/ x3 y) `0 t+ X) J
39
" Y6 Q M3 g' j% T A8 r* J2 f
40
) X8 V9 f. m. F5 T/ U L
41
4 @7 B% ?9 [4 x9 k$ J& v5 z
42
M6 C+ @+ r% k
43
3 [6 c# @2 W2 W- O- b* ?7 N
44
) e |0 Z( p4 k
45
( f. @9 j- m8 T4 [
归并排序的特性总结:
: x( J8 g3 ~, O
1 u' c7 C& I5 o1 ~! l
归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
* D. w( y" \! B o4 q0 k. L
时间复杂度:O(N*logN)
! T6 N5 v+ S( V# H8 }
空间复杂度:O(N)
' b+ G/ l) _& [
稳定性:稳定
* E3 i- m7 Z9 g. k( d6 y
8 T8 ^9 _% @8 G% C7 L% v
————————————————
/ S$ f1 j6 n( O, i( u
版权声明:本文为CSDN博主「桦秋静」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
% Z4 ~) J' V! n9 c
原文链接:https://blog.csdn.net/weixin_61561736/article/details/126796657
6 {# H9 `* D% S/ k1 i, m/ {( y
- ?& p- r8 ~* q" z8 K) c
9 c5 l( ?! S& g; {( E5 q5 z U+ k
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5