Bug记录-01
教材: 十二五规划教材 数据结构 (第2版) 陈越主编
问题描述
今天在写学校的快速排序,闲的没事打算用异或交换算法(指针实现)和指针实现快速排序,发现自己的排序序列莫名其妙会有一些元素变成0;
异或交换算法(指针实现)
看起来人畜无害的样子
void xor_swap(int *a,int *b)
{
*a^=*b;
*b^=*a;
*a^=*b;
return;
}
指针版的快速排序算法
void Qsort_p(int arr[], int *left, int *right, const int len)
{
const int cutoff = 3;
if (cutoff <= right - left) // 调用快排
{
Median(arr, left, right); // 按照中值标准选取主元,并将主元置于倒数第2个位置
int *pivot = right - 1; // 主元
int *low = left, *high = right - 1; // 左端起始和右端起始
while (1) // 因为 arr[i] 相当于*(arr+i) 因此直接操作指针在快排时会比使用变址运算符[]更快,每次都减少一次加法运算
{
/*
while (*low < *pivot)
low++;
while (*high > *pivot)
high--;
*/ //这样写运气好的话可能会进死循环 当*pivot=*high=*low
// test_200_d.txt的数据
// 显然*++low 和*low++ 这2种写法也行不通)
while(*++low<*pivot);
while(*--high>*pivot) ;
if (low < high)
xor_swap(low, high); // 交换2个数
else
break;
}
xor_swap(low, pivot);
if (!bench_test)
print_arr(arr, len); // 非基准测试下打印过程
Qsort_p(arr, left, low - 1, len); // 递归解决左边
Qsort_p(arr, low + 1, right, len); // 递归解决右边
}
else
{
insertion_sort(left, right - left + 1); // 规模过小调用插入排序
}
return;
}
中值基准选取
void Median(int arr[],int *left,int *right)
{
const int middle=(left-arr+right-arr)/2;
if(*left>arr[middle]) xor_swap(left,&arr[middle]);
if(*left>*right) xor_swap(left,right);
if(arr[middle]>*right) xor_swap(&arr[middle],right);
xor_swap(&arr[middle],right-1); //发现问题的位置
return ;
}
原因分析:
根本原因是对同一个地址进行了自我异或,导致该地址上的数字变成了0
一开始并没有往xor_swap上想,毕竟这个程序里还跑了冒泡和堆排序,都使用了xor_swap进行异或交换,但都没有发生问题。
定位问题
可以看出在进入xor_swap前 arr中还没有0出现,并且快排算法仍在正常运行。
但是进入xor_swap并完成第一次异或操作后发现两个操作数都变成了0;
这时候看了一眼两个操作数的地址,于是恍然大悟
xor交换的基本原理就是利用两个数的存储位置不同,一个数的部分二进制位可能会暂时为0,直到异或后再恢复/交换。
但是如果两个数的存储位置完全一致,暂时的为0 就变成了永久的,后面怎么异或都回不来了。毕竟0^0=0;
解决方案:
加个条件判断即可,地址相同的数本身就不需要交换
void xor_swap(int *a,int *b)
{
if(a!=b) //地址相同不需要交换 ,同一个地址自我异或会产生0
{
*a^=*b;
*b^=*a;
*a^=*b;
}
return;
}
指针版快速排序正常运行
下一步就剩内存泄漏检查和基准测试就可以交报告了。