; H* p- |- \/ r$ E& U( R6 u3 P6 f 我们考虑新开一个数组来放入排序好的值,要不改变顺序的话就要用尾插,让nums1和nums2数组元素的较小值尾插到新数组中,两个数组总会有一个先插完,另一个数组就把剩下的全部尾插接在新数组后面。 ! L- E7 u1 N1 b* P7 x0 S7 b' }, D* N& f' V
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gsgj7Cmx-1662793985599)(https://typora-picture-1313051246.cos.ap-beijing.myqcloud.com/归并原理.gif)]4 H0 p4 R) o- M j$ | U& t+ A: s
, E \4 |1 d6 \$ nint* merge(int* nums1, int m, int* nums2, int n)+ u: y5 x: l) T! N
{" K! W* b4 j2 D
int* arr = (int*)malloc((m + n)); 8 ?5 P; r _; a$ o if(arr == NULL)9 h7 Z/ L0 f* l" M
{ ! C2 z6 ^5 D; w2 ], @$ \7 z6 v perror("malloc fail");# o2 j+ p5 H" F9 ^
return;) J% b/ g X0 q/ X9 ?8 b+ H
} 1 U! s* @& O' H5 K' P0 P8 r; v+ \, J; O1 w$ Y. J f5 `
int p1 = 0;; S9 u0 J, u3 r
int p2 = 0;+ K" P# P8 T# L5 w. T
int cnt = 0; 1 [/ ]) R6 _+ r5 V! [9 I& W while(p1 < m && p2 < n) ) p% s. M8 r. s2 ~! E4 R { 6 T8 U! P% v6 f# @' r/ m& q. N* f if(nums1[p1] < nums2[p2]) ' }- }- \2 S* F2 J$ y3 ? {% A4 G" A7 S! s# i$ O0 g. I+ m
arr[cnt++] = nums1[p1++];$ B" S" z0 z" z4 Q& y
} 5 Z, V% [0 e( Y+ |. ? else 0 M' s( V8 \+ ?- I9 B: z {' [9 f! g# `9 k
arr[cnt++] = nums2[p2++]; 2 T7 a2 r: B- V) n/ `$ m* \ }! W7 r3 [* U1 T' `: U
} ( T/ z/ h5 ?# W, K7 Z while(p1 < m)+ g% q' w* }2 k3 D. G9 X2 [
arr[cnt++] = nums1[p1++]; ' E3 K2 T$ F+ e3 L7 @& \ $ d3 w; J: {4 i% P# Q6 }2 Z while(p2 < n)+ c) S! |) O2 v4 L7 O
arr[cnt++] = nums2[p2++];( Y8 `* @ L6 W/ ^1 s! W
/ p5 g( J) ]% n% i6 {3 y return arr;- Z# j6 _2 n7 u% R8 _4 A6 Z* U3 j
} " O: M+ a9 P7 |7 l2 D0 |- ~4 J+ ?* b9 I* I3 T
18 B7 A/ v3 X. o7 m* S. h
2; V1 |( c1 W- K; x8 E. K; ^( T
39 E* W$ J; X, v, U& [
4 ! Q: Y6 z8 x R! a# Z6 x8 I2 a5& _+ Y% F% A. ~ l! A0 V0 O
60 h" O4 ]3 C& x8 _2 |) f0 {
7 $ u0 W9 J# L4 \8) X& y3 \% d! m( J1 X3 Y
9" ~# |; U# c- R
10 & Z1 F% R4 {8 }* h112 O7 V! K8 O0 n
12' @9 N1 i8 `6 _ Z# J/ |# B3 _$ H/ v
13' ]' Y4 g( Z C2 I' A; y
14, V. J) Q1 f& q5 H! m7 A
15; B2 `+ Z0 E& ]# O- ]3 p0 |! |( Z
16 6 v: ~$ `- g& D( `/ e# y( U" w$ M3 E17 9 E/ t* v' G5 y" Q. l5 ]18 5 [$ L7 m. N% W$ f3 I# b4 i1 z19# t! c6 U* B4 { t
209 B8 v/ T& t" x) C# D' M! ~
21 # ?4 c+ v+ Z/ i6 a7 C$ ~229 M9 x( O! H$ }+ x8 V1 A0 m
23 ; O2 B2 l# }- f; {24. e! L7 ~8 I7 R$ ^# q, ?( O
257 z; u) P! k0 V7 h* L
26 . b: D) A* I7 ]; _27$ g2 L: L2 _8 T8 i; w% ?# p
289 W* n- e; |+ n3 m! Q) D
29; ~2 c) A2 P' b) v
30* I' i1 y, b! S
31 7 o# q* W M, `! Z; ?9 k/ Q3 K# @ 所谓的合并就是利用这样尾插的思路,我们就想到要把原数组分解成两个有序子序列来合并,那么如何将原数组分解成有序子序列呢?容易想到用递归,其实非递归(迭代)也能实现,我们接下来具体来看看实现方法。" W; g+ A7 s3 O7 _, w5 A, K. l/ P# s# U
! T! Z: u. Y2 d& O0 B3 U4 z
递归实现 ) J, j' W" a% E% i' ^8 C; Z 通过二分分割出两个子序列,然后进行递归,先左后右,不断分割直到子序列仅有一个元素时子序列一定有序,这时候就可以往回退了,等到左右子序列都退回后就可以归并了,不过不能直接归并到原数组,因为会覆盖而丢失值,不妨归并到另一个辅助数组,归并后再拷贝回原数组,思想就是前面讲的合并的思想。( ]! ~. {+ s; ]. A" _8 U
! _4 A8 ?$ n) a* C' F2 {! A4 `
3 v0 E) ~& p! }
Y7 R0 h. S5 c4 }. a" \
" R3 k$ e# E( U9 w4 G9 r
1 R @- n/ X2 e: s, V3 o' Yvoid _MergeSort(int* arr, int* tmp, int left, int right)3 q8 r0 m) k% i& M" N2 u, ]. G% i9 z
{ 1 T& ?% ~9 _: ` l; Q: C4 H; r assert(arr);7 N/ ^' q, u- L; l, x
" R9 ^& J* Q! ^8 }7 V+ L" \( t% D if (left >= right)//递归结束条件不要漏了 6 }) i/ R# O- O6 k return; ( g! M& t2 g( X6 X8 u" `* F3 \) h( _ Z3 K* j
int mid = (right - left) / 2 + left; + ~0 O4 V* N8 d' o4 O v v $ P0 m2 q' p; Z. O! C //划分左右子区间[left, mid]和[mid + 1, right]2 m$ H0 o y# T) R" v
_MergeSort(arr, tmp, left, mid);: f2 F& O1 }9 f* Q7 R2 y
_MergeSort(arr, tmp, mid + 1, right); 2 u: y8 f: v* n0 E( J $ l( f6 b2 ?! P1 @6 ^. k) j' Q- x //归并 : V( k2 N) D3 X. m int begin1 = left, end1 = mid;) U5 ]) W/ V+ h/ x5 L [" j: b
int begin2 = mid + 1, end2 = right; 4 W8 H( J, l8 ]; h0 @$ A5 m int i = left; : A$ e. @$ E i$ t8 K9 \ while (begin1 <= end1 && begin2 <= end2)* _! b- }& y( }: o" s6 |
{/ j0 l+ F) Y" d3 D$ {4 W- U' X
if (arr[begin1] < arr[begin2])6 `. u/ [7 b# @2 D! z
tmp[i++] = arr[begin1++]; 1 Q0 T( t3 S7 E) z( U) t( D else ' o; n6 U. E2 `8 _8 y/ p tmp[i++] = arr[begin2++];8 U# m) W" L( C% [
} 2 \- y8 _5 p2 k3 n4 v0 @) h: S9 r5 Q: w
while (begin1 <= end1) 5 L8 z+ O7 Q" G& r) o8 U$ D/ W' k tmp[i++] = arr[begin1++];6 c# E- `" `8 `' x7 ~% u6 J
while (begin2 <= end2) ) c6 t8 f! w8 M) L Z tmp[i++] = arr[begin2++];/ T* d2 O. a/ Q6 _$ c/ A
. I& k, w; s+ d3 |7 V
//拷贝回原数组——归并哪部分就拷贝哪部分回去 % C% @- A/ l/ O' S: Y3 r; W7 g //而不是拷贝整个数组回去 * Y, A! b( t/ A& R- j memcpy(arr + left, tmp + left, sizeof(int) * (right - left + 1));/ i5 Y7 _$ }- V& @- F
} 6 m0 D) j& r! ?6 n/ k; u; v1 x 7 |) U" i" f' s7 c2 x1 `8 f ?void MergeSort(int* arr, int left, int right) $ U" F7 R- Y" C% @; Q! Z" h" h{) {# Z7 D4 R6 Y# d6 j( \/ O. `
assert(arr);3 T8 _7 Z: n$ j; C2 {
$ r" ]+ b3 a. G! u- ]
int* tmp = (int*)malloc((right - left + 1) * sizeof(int));& ^; M8 v2 r1 T9 r# ?) k
if (tmp == NULL)) p+ ~3 [" H! t( G4 P
{ a, v$ J, n$ K2 L T3 ~# \8 G2 {& A
perror("malloc fail"); 4 ?% X2 q2 C6 w! b n v2 f return; : c) H2 `4 m# I }8 x1 y1 b! C$ y3 j: I6 Y! v
! J; V: A* X$ k# O5 {
_MergeSort(arr, tmp, left, right);9 k: K$ u/ W: N9 x) {
! x2 u8 [7 X3 n free(tmp);- Y y5 ], g& z7 b# |
tmp = NULL;9 e: s2 ?3 |- N
} 4 P& b& z) @* C% k; h$ \2 k! t9 J* t
10 p: Z f% A# d, X' Y- B, G
2 : C o* j8 f, J/ k8 R' k" q3 x3 ; _$ p$ L4 b3 m' I4 " b- @: F( u( R! U( T- I' H9 S58 A' y3 }5 x/ p" c6 N3 F
6 / `5 {* t1 k3 I7 J! w7 3 ?/ ~2 r' h, E, G4 R3 O8. O% \/ G8 E* }
9/ i# J0 }4 N0 c2 `- Y
10 ! S1 K9 z, a" }& F J11 q9 k% v& b2 @/ P* i. T
12 3 M5 X# \. J: J- p! w13- V$ m2 H( @8 B
14 0 f& \2 D4 |4 r% a6 d15 0 {, c/ K& {+ s8 L: R16 " O. [/ F) f9 c, n177 G& ~ T' |8 p4 M4 `3 V: _9 b
18 % o5 T1 H9 e) S2 |& e9 G19 3 o! N: G8 |; ^; C4 ]207 ~# l+ K+ E7 ]3 B- R0 D
21 4 b7 @9 W# f1 Q, R. j8 E22$ Z8 M+ ?: A4 }% f
23 8 C! [) t4 G) _1 i& K) w24" Z$ X% F9 o) W
25 ) t5 k8 p+ k( u4 W0 {6 L* q26 : [! f3 D( _2 O2 D5 _$ K' g# T& k4 f' G27 1 e& a1 r) Y( W3 b$ L28( }/ E5 b* d* X5 x/ Z4 K
29 . g+ P! ^ ?2 b8 o- E0 E Z308 e. A4 \8 m5 Q: k
314 c- v6 S" r7 X' P7 ]9 I
32 v2 `. b* \% W# ^. `4 t1 j8 U33 ' B& C6 j3 C% @/ Q: T346 [7 V& u) P+ E# C+ `4 z3 A9 M7 ]
35 6 y- `: L- L/ j' c% X: N @36 O4 F; h1 G0 |6 F
37 8 _8 g0 S$ v1 f0 ~' J+ d8 M38' z0 D) ]; a; X# P8 T0 }- k3 W
39 , h3 U; N0 K# y a40 : e4 ]3 z2 n" e, R( u& ~) M41 9 }9 u7 k( y& d" u! x42 ! z- F+ V. i7 W$ u9 k43 }5 c3 {+ i+ ?: z( A" h
44: z0 z9 D k) s7 |
45 ) Y* @- Q/ ?; x! g. e" l463 ? o x7 X% n |% Z$ D1 m
47 * b0 q% V! E# |4 k48* i- y% F0 a! x O, y7 |
49' o- h' j3 p! m) Q( F
50 6 G7 T; M8 o1 A$ ]51& p4 O7 `- L/ D: v, x
非递归实现0 X& N% x; _1 ^ f
直接在原数组基础上归并,设gap是子区间元素个数,从gap = 1开始,因为仅有一个元素的子区间一定有序。为了方便,我们把gap=1叫做第一层,以此类推。 + Y8 _2 o6 v2 g$ s( N # {) f4 ?! ?( \& H$ _: x* p6 n5 X7 W4 F6 d
% D/ N& F1 l; c' N9 D4 D
不同的gap值代表所在层数不同,每一层都是从左到右两组为一对地取对配对归并,i就是每对起始位置,之所以更新i的时候要i += 2 * gap是因为每队两组、每组gap个元素,所以要让i跑到下一对的起始位置的话不就要跳过一整对的空间嘛。 9 @, x# l% ^5 Q# S0 [ 0 Z7 H0 \, {' i 还要注意区间的取值,每个区间就是一组,就有gap个元素。2 z- q& T+ Q3 e7 W9 k
' W2 r. U' A3 ~# r) V7 R4 I
整体拷贝遇到越界就会比较难搞,所以我们这里用部分拷贝的思路,每次归并后直接拷贝,要注意一下指针偏移量不是begin1而是i,因为begin1已经在归并过程中被改变了。 2 i0 w5 @$ `% B5 T: p0 }& C1 B6 d+ s . Z1 `$ i9 `6 u# i2 ^5 K% x代码实现 p3 a1 P4 s3 s7 {5 ~) ~; I8 w5 b! U& n, W- S$ h
void MergeSortNonR(int* arr, int sz)+ R' p4 O4 r/ d% y/ n
{ ^' O0 H+ w2 R5 ~1 x, J4 Q2 k( i assert(arr);8 B3 @" u7 {8 y+ I( E. u
' i# H* j$ X: o, L int* tmp = (int*)malloc(sz * sizeof(int));/ K0 ^# ~; Q7 G1 D
if (tmp == NULL)* a+ \) b3 ?0 M/ J
{ + [* J- \% N: e: [8 U" t perror("malloc fail"); 3 X7 H' b; }, N2 R. n return;: s3 v& @: c6 @: s, v# k/ a6 S1 {
}- x; t* }% `9 \6 O& }3 c( Z
[( _6 N1 U. h+ V3 G
int gap = 1;8 F# y7 ?, N! @& N3 U
while (gap < sz)9 {! o% r3 b5 k$ x. L% r
{3 w& w4 @) R5 c; e2 O. m
for (int i = 0; i < sz; i += 2 * gap)) U- J0 y; K% T! O; L% D9 S% {
{ " }4 u' G2 S1 K5 F8 B. l0 `5 ]1 J int begin1 = i, end1 = begin1 + gap - 1; 5 y0 m# Y( J/ A int begin2 = end1 + 1, end2 = begin2 + gap - 1; * W$ p0 w i2 e) U: \ int j = begin1; ' y: x$ P% N/ T! j# S& a8 g # c# f0 p6 i! S0 y6 t, _2 G1 A3 r //归并7 d) ~: F( B: y- X# i' l
while (begin1 <= end1 && begin2 <= end2)3 A: ?0 a, l |; ?
{ ' d% H4 i- I6 Q if (arr[begin1] < arr[begin2])6 E6 X/ H2 t. o9 m9 w
tmp[j++] = arr[begin1++];: P3 t* X3 j7 \4 X
else 3 D, X' x/ F* V$ M+ a L7 [4 q) ] tmp[j++] = arr[begin2++]; : r) F# x8 r9 S o) Z. p$ U }8 o7 P5 h% Q( H( U( G& r! c8 H8 ~
! z" [: b8 L/ i' Y: x
while (begin1 <= end1). j1 ?9 c9 }* i, J
tmp[j++] = arr[begin1++];, j1 K6 Q3 j* h# k0 D S
while (begin2 <= end2)5 p1 M% O" W v( d& t9 Y9 S! [3 c
tmp[j++] = arr[begin2++]; % p2 c w/ ?1 G0 J7 P$ i. `7 I' w+ v4 p' D
//拷贝回原数组——归并哪部分就拷贝哪部分回去 ( v, V+ y6 L" ` memcpy(arr + i, tmp + i, sizeof(int) * (end2 - i + 1)); " f. f$ L; ]( V } 7 p. K |. c' ~2 l# x! o gap *= 2; ) M$ C6 S K4 T# p; K/ Y }9 P- }$ X4 q- ~/ q% }6 F0 S% Z
: W0 z5 j; j* u
} + y6 K4 K" Q2 O: q) r 7 x: \/ P1 ?- Y! B4 e) c13 A. G1 E$ C" N$ p$ {
2* A$ r. a7 p& u7 K+ ?+ |
30 S3 U. ]! r: `: C8 D1 q& `
4 ' C. h T6 U/ w; p5 + k0 Z: ^$ Q' a0 w7 ^! ]68 T$ K# q$ V: W. Z- }
7 ' T: |- L6 p# o% Z9 x8# |" r/ p" Q* D. @# |) h0 k' ]; |
9* {2 H. j5 |. O' i$ w2 R% L3 y: m
10+ T. H o6 s: R
11 ' c4 n: a" `9 M, F% F12# Q; C3 Q7 ~$ _$ m3 Q
13' a1 R/ A- V+ B5 H# S
14 8 D/ z8 t, o+ O: p4 G% `15 7 G* w) L/ g' E1 n16' G' D, v0 U0 M
17 ( M$ j6 [" m2 i- C18. n, e% m8 i, x# Q# s+ d
19. X/ ?' R) V6 ?* G' L, j7 d$ H' W
20 : }: O* o5 Y7 M21 $ P; R' v" Z! |) z22 8 r4 J3 |( |, D" ^23 / x" a. M1 E; E, C24( q+ s. F' Y4 a! ]
25' }( ^5 h) k+ r/ l
26 4 n1 }# j& d/ \27 6 E' a1 c1 p1 _: C- i4 J; }8 l7 y) {28* X, {/ @" B0 N7 o# a9 k
29 ) ]" H' v" I" T; z% R304 r, D. a0 x$ p
31 V4 V' [; R$ N- `
323 [$ L( o/ u$ U; m
33- E8 ?: S& G+ ]* ]4 L
34, F5 {& o( K( e7 _' J- A
35/ C. W0 L+ H" P4 f$ P' v" r3 g
36% M4 i. [& G+ f" k; \- R
37; D1 b2 l8 C# `9 l# t
38 ! |; D& @2 \7 n39 3 U5 W/ Z5 {0 N9 r+ M& j7 J+ v8 G40( [% c6 v; j- p ]3 d& W) N2 ]
413 K1 L6 r; ^# [: x2 L% x1 E: Y1 U4 g
边界问题 6 N, z! Z. k k) D# c 实际上还需考虑是否越界的问题,上面那段代码并没有考虑,所以还需一些改进。为什么会存在越界的可能呢?因为我们是以gap的整数倍去取区间来归并的,而区间个数不一定总能满足两两配对。 ^& L" R! `0 t
! T9 f0 ^" C0 s/ O7 s$ p0 u2 _$ h# t3 w/ V
举个例子,就把前面的那个数组后面加上个元素5,没有越界检测时出现的情况: 5 t p7 J% w# _: v( f & A$ p# w! Q) d/ X" {6 A, ^/ Q: e( Z b: ~6 d1 Q+ R" \2 S
# K0 j& Z* x8 ?( m8 [由上图可知越界分为三类(这里将[begin1, end1]、[begin2, end2]分别作为第一和第二组) / E7 D `9 x* V5 d% r. i/ C3 l8 O$ W) V! P. h4 d% b
第一组越界(即end1越界) 1 ~1 H1 F5 r3 Q( S% J( r 4 p# H& t9 p# ]# C2 g3 H应对方法:这种情况一般介于第一层和最后一层之间,break跳出for循环,不让越界值被访问。 % y1 [! P1 [9 j; T7 {9 s . P& m" D1 E" R' y2 n+ T5 r第二组全部越界(即begin2和end2越界)+ L9 A( I# B- V; E
* Z p! E4 C( R% I8 |
应对方法:这种情况一般在第一层,break跳出for循环,不让越界值被访问。& s7 d, V6 @& |+ i/ s