C++ 归并排序
归并排序本身的思路很容易理解,也很适合用递归实现,但递归实现有缺陷,就是会有栈溢出的风险,递归深度有限。递归能实现的都可以转换成迭代实现,迭代实现其实就是把分解的步骤省了。
查了网上归并排序迭代的实现,很多都是 left_min、 left_max、 right_min、 right_max,然后 right_min 跟 left_max 都指向左部分的最后一个元素, right_max 则是指向右部分的最后一个元素的下一个位置,我觉得这样 min 和 max 的指向前后不一致,增加理解成本,所以下面的实现尽量保证不出现这样的问题,主要是避免日后回顾时看得费劲。
迭代实现
1 |
|

1️⃣ for( int i = 1; i < length; i *= 2 )
i代表待归并的部分的应有元素个数,i=1就是1个元素和1个元素进行归并,i=2就是2个元素和2个元素进行归并。为什么说是应有元素个数,因为最后一份是可能不够数量的,例如一共3个元素
[1,2,3],当i=2时,就会是[1,2]和[3]。i<length,因为如果i=length,那么第一部分有全部的元素,第二部分零个元素,那就没有归并的意义,或者说已经排完了。
2️⃣ for( l = 0; l < length - i; l = re + 1 )
l指向左部分的第一个元素,re指向右部分的最后一个元素,所以re+1就是下一个左部分的第一个元素。l<length-i
首先想一想退出循环的条件,当什么时候应该退出循环呢,是不是当只有左部分而没有右部分的时候呢,因为需要有两个部分才能进行归并,退一步说,只有左部分时也进行归并,那么它该和谁归并呢,显然没有意义,所以当只有左部分时就应该退出循环。该情况下,设定r是右部分的第一个元素,如果左部分是满i个元素的话,r=length,见图0,如果左部分不满i个元素,那么r>length,见图1,所以r>=length。而r=l+i,所以l+i>=length,也就是l>=length-i的时候应该退出循环,即l<length-i是循环条件。


3️⃣ le = l + i - 1; r = le + 1; re = min(r + i - 1, length - 1);
le = l + i - 1;l+i指向右部分的第一个元素,所以l+i-1指向的就是左部分的最后一个元素。r = le + 1;
上面的le指向左部分的最后一个元素,所以le+1就是右部分的第一个元素,也可以直接写l+i。re = min(r + i - 1, length - 1);
同理r+i指向下一个左部分的第一个元素,所以r+i-1指向的就是右部分的最后一个元素。但这是右部分能凑齐当前i数量元素的情况下,凑不齐的情况下,
re就只能指向容器的最后一个元素,无论右部分有几个元素。le、r为什么不需要考虑这个问题,因为该层循环的条件是l<length-i,这个取值范围的前提就是肯定有右部分,le、r就一定不会越界。
4️⃣ while( r <= re ){ temp[te++] = vec[r++]; }
因为说明剩余的就是两部分中最大的几个元素,而且已经有序,放进 temp 中,等下也是原样赋值回来,为了一致,方便理解,还是保留了。