由快速排序引起的递归问题
发现问题
在写快速排序算法过程中,有了一个疑问,以下是调试正确可以排序的:
#include <iostream>
using namespace std;
int a[10] = {5, 9, 3, 1, 2, 8, 4, 7, 6, 10};
void quick_sort(int left, int right) {
int base, i, j, temp;
if (left > right) {
return ;
}
base = a[left];
i = left;
j = right;
while (i != j) {
while ((a[j] >= base) && (i < j)) {
j--;
}
while ((a[i] <= base) && (i < j)) {
i++;
}
if (i < j) {
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
a[left] = a[i];
a[i] = base;
quick_sort(left, i-1);//注意这里的 i-1 和 i--的区别,i--是改变了i的值
quick_sort(i+1, right);
return ;
}
int main(void) {
quick_sort(0, 9);
for (i = 0; i < 10; i++) {
cout << a[i] << " ";
}
cout << endl;
return 0;
}
在正确之后,我想到能否将以下这段代码
quick_sort(left, i-1);
quick_sort(i+1, right);
换一种写法:
quick_sort(left, --i);
quick_sort(++i, right);
结果直接挂了,问题由此产生,纳闷有啥区别为啥会挂呢?这里的形参“i-1”和“–i”,这里的区别在于 i 本身的数值第一种是没有发生变化,而第二种写法 i 本身已经变了。但是这个地方貌似也不影响最终的结果啊,当然后期通过验证,第一句这么改确实没问题,问题出在第二句上面。在此因为疑惑,就查了很多递归的样例,用一个简单的样例来说明下问题到底出在哪里。
解决问题
一个典型的递归算法解决阶乘的代码:
int Factorial(int n) {
cout << &n << endl;
cout << n << endl;
if (n == 1) {
return 1;
}
// return (n * Factorial(n - 1));// ---写法 A
// return (n * Factorial(--n));// ---写法 B
}
int main(void) {
// cout << Factorial(3) << endl;
// cout << Factorial(4) << endl;
return 0;
}
很显然看着“写法 A”是实现了我们的阶乘想法,可是“写法 B”究竟错在哪里呢?
因此我将过程简单的分析了下,“写法 A”比较直观,没什么好多说,“写法 B”为什么如此,其实是花了好长时间才想明白的。
“写法 A”过程分析:F(3) = 3 * F(2) = 3 * (2 * F(1) ) = 3 * 2 * 1;
“写法 B”过程分析:F(3) = 2 * F(2) = 2 * (1 * F(1) ) = 2 * 1 * 1;
通过上面的样例可以看到“写法 B”的过程中由于改变了“n”的值,所以导致前面的“n”也变了,所以最终出来的结果是不对的。
当然想明白这点通过了好多种验证,最初的疑惑源头是认为形参的值是单独独立存储的,传入的只是一个值而已,两种传法的值都是一样的,因此觉得怎么传都行,调试时打印取了地址,发现确实如此,认为值这么传没有问题,但是最后发现问题是出在了这个“n”在递归过程中再次被使用了,它是和下一次递归条件密切相关的,所以就出现了问题。
为了验证,又做了一个测试,数据正确。
“写法 B”过程分析:F(4) = 3 * F(3) = 3 * (2 * F(2) ) = 3 * (2 * (1 * F(1) ) ) = 3 * 2 * 1 * 1;
至此原理已搞清楚,那么回到最初的快速排序问题上,将代码改进下,就OK了。
quick_sort(left, --i);
++i;
quick_sort(++i, right);
以上过程的解决还是耗费了一些时间的,开始把问题放到了一个算法交流群里,经过多人交流调试后才最终搞明白,在此也是把过程简单的记录下,如有什么写错的,感谢交流指导~