如何在C++中不使用反转函数实现回文数递归调用?代码解析请求
嘿,我来帮你把这段看起来有点绕的递归代码拆解开,一步步捋清楚它到底是怎么工作的!首先先把完整代码贴出来方便对照:
int onedigit(int a) { return (a>=0 && a<10); } bool isPalUtil(int num, int *duplicate) { if(onedigit(num)) return (num == (*duplicate)%10); if(!isPalUtil(num/10, duplicate)) return false; *duplicate /= 10; return (num%10 == (*duplicate)%10); } int isPal(int x) { if(x < 0) x = -x; int *dup = new int(x); return isPalUtil(x,dup); }
1. 先搞懂每个函数的分工
先逐个拆解每个函数的作用,建立整体认知:
onedigit(int a):这是个简单的辅助工具,用来判断输入的数字是不是个位数(0-9之间),返回的是布尔值转成的整数(C++里true对应1,false对应0)。isPal(int x):这是整个功能的入口函数。首先处理负数——因为回文数不区分正负(比如-121和121的回文属性是一样的),所以先把负数转成正数。然后它创建了一个原数的副本指针,把原数和这个副本指针传给核心的递归工具函数isPalUtil。isPalUtil(int num, int *duplicate):这就是最让人困惑的核心递归函数,我们下面重点拆解它的逻辑。
2. 核心递归函数isPalUtil的逻辑拆解
这个函数的核心思路是用递归把原数逐步“砍”到个位数,同时通过副本指针从右往左“剥”数字,然后从递归的最深层开始,逐一对比原数的高位和副本的低位。
我们把它的逻辑拆成3个关键步骤:
第一步:递归终止条件
当num是个位数的时候,直接对比这个个位数和duplicate的最后一位((*duplicate)%10)。这一步相当于先锚定整个数字最中间的那个数(如果是奇数位的回文),或者最中间的一对数的其中一个(如果是偶数位)。
第二步:递归深入
如果num不是个位数,先递归调用isPalUtil(num/10, duplicate)——这一步是把num去掉最后一位,继续往递归的深层走,直到num变成个位数。
这里有个非常重要的点:递归的执行是“先深入再回溯”的,而且只有当深层的递归返回true时,才会执行后面的代码。如果深层递归返回false,会直接把false逐层传递回去,整个判断就终止了。
第三步:回溯时的对比
当深层递归返回true后,我们把duplicate除以10(相当于去掉它的最后一位),然后对比当前num的最后一位(num%10)和duplicate的最后一位((*duplicate)%10)。这一步就是在对比原数的高位和副本的低位。
3. 干运行演示:用实际数字走一遍流程
光说逻辑可能还是抽象,我们拿两个实际数字来走一遍,你就能彻底明白:
示例1:回文数1221
- 调用
isPal(1221):因为是正数,创建dup指针指向1221,接着调用isPalUtil(1221, &1221)。 isPalUtil(1221, &1221):1221不是个位数,递归调用isPalUtil(122, &1221)。isPalUtil(122, &1221):122不是个位数,递归调用isPalUtil(12, &1221)。isPalUtil(12, &1221):12不是个位数,递归调用isPalUtil(1, &1221)。isPalUtil(1, &1221):1是个位数,对比1和1221%10=1,返回true。- 回到
isPalUtil(12, &1221):深层返回true,执行*dup /=10,dup变成122。然后对比12%10=2和122%10=2,返回true。 - 回到
isPalUtil(122, &1221):深层返回true,执行*dup /=10,dup变成12。对比122%10=2和12%10=2,返回true。 - 回到
isPalUtil(1221, &1221):深层返回true,执行*dup /=10,dup变成1。对比1221%10=1和1%10=1,返回true。 - 最后
isPal(1221)返回true,判断这是个回文数。
示例2:非回文数1234
- 调用
isPal(1234),创建dup指向1234,调用isPalUtil(1234, &1234)。 - 递归深入到
isPalUtil(1, &1234):1是个位数,对比1和1234%10=4,返回false。 - 这个
false会直接逐层传递回去,所有上层递归都直接返回false,最终isPal(1234)返回false,判断不是回文数。
4. 总结一下
这段代码的巧妙(或者说让人一开始困惑)的点在于利用递归的回溯特性,不需要额外反转整个数字,而是通过递归的深度和副本的逐步缩小,实现原数高位和副本低位的逐一对比。本质上是用递归代替了循环的反转操作,只是因为递归的“先深入后回溯”特性,看起来有点绕而已。
内容的提问来源于stack exchange,提问作者Deepnarayan Biswas




