在实现归并排序的非递归方式,遇到了一个bug。事故现场如下:
//传入三个点,因为待归并的两个有序数组长度可能不同
void merge_un(vector<int>&vec, vector<int>&ans,
int left_start, int right_start, int right_end) {
int left_end = right_start - 1;
int len = right_end - left_start;
int index = left_start;
while (left_start <= left_end && right_start <= right_end) {
ans[index++] = vec[left_start] <= vec[right_start] ?
vec[left_start++] : vec[right_start++];
}
while (left_start <= left_end) ans[index++] = vec[left_start++];
while (right_start <= right_end) ans[index++] = vec[right_start++];
while (len--)
vec[right_end--] = ans[right_end];
}
//两两归并相邻有序子列
void merge_pass(vector<int>&vec, vector<int>&ans, int len) {
int i, j;
//int lenn = ans.size();
//每次循环,i=i+2*len,因为归并的两段总长度为2*len
//每次都跳过归并好的那一段,然后去寻找下一个两段进行归并
//i<ans.size()-2*len,保证了前面成对的都处理完
//对于尾巴,要另外处理。尾巴可能是两块不一样长的,也可能只有一块
for (i = 0; i <= (ans.size() - 2 * len); i += 2 * len) {
if (len == 8)
int a = 0;
merge_un(vec, ans, i, i + len, i + 2 * len - 1);
}
//说明尾巴有两块,一块长度为len,另一块长度为vec.size()-i-len
if (i + len < vec.size())
merge_un(vec, ans, i, i + len, vec.size() - 1);
else //说明尾巴只有一块,那么直接拷贝即可
for (j = i; j < vec.size(); j++) ans[j] = vec[j];
}
//自底向上,非递归实现
void merge_sort_un(vector<int>vec) {
vector<int>ans(vec.size());
int len = 1;
while (len < vec.size()) {
merge_pass(vec, ans, len);
len *= 2;
merge_pass(ans, vec, len);
len *= 2;
}
for (auto i : vec)
cout << i << " ";
cout << endl;
}
看起来干干净净的小绵羊,居然在运行的时候报错了!
认真分析了半天,并没有任何边界的问题,但是为什么会发生指针越界呢?于是乎我开始抓bug,终于发现,bug发生的情况是:
当len=8,ans.size()=9时,居然能在断点处——int a=0; 停住了!!!所以是因为这里引起的指针越界的(越界情况发生在merge_un函数中)
//两两归并相邻有序子列
void merge_pass(vector<int>&vec, vector<int>&ans, int len) {
。。。
for (i = 0; i <= (ans.size() - 2 * len); i += 2 * len) {
if (len == 8)
int a = 0;
merge_un(vec, ans, i, i + len, i + 2 * len - 1);
}
。。。
}
百思不得其解。9-2*8=-7才对啊,怎么会执行到这一步呢?怪自己稀松了,忽视了vector的size()返回值是unsigned int !!!
哭了,这种低级错误以后不要再犯了。谨记。