You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在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):这是整个功能的入口函数。首先处理负数——因为回文数不区分正负(比如-121121的回文属性是一样的),所以先把负数转成正数。然后它创建了一个原数的副本指针,把原数和这个副本指针传给核心的递归工具函数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

  1. 调用isPal(1221):因为是正数,创建dup指针指向1221,接着调用isPalUtil(1221, &1221)
  2. isPalUtil(1221, &1221):1221不是个位数,递归调用isPalUtil(122, &1221)
  3. isPalUtil(122, &1221):122不是个位数,递归调用isPalUtil(12, &1221)
  4. isPalUtil(12, &1221):12不是个位数,递归调用isPalUtil(1, &1221)
  5. isPalUtil(1, &1221):1是个位数,对比11221%10=1,返回true
  6. 回到isPalUtil(12, &1221):深层返回true,执行*dup /=10dup变成122。然后对比12%10=2122%10=2,返回true
  7. 回到isPalUtil(122, &1221):深层返回true,执行*dup /=10dup变成12。对比122%10=212%10=2,返回true
  8. 回到isPalUtil(1221, &1221):深层返回true,执行*dup /=10dup变成1。对比1221%10=11%10=1,返回true
  9. 最后isPal(1221)返回true,判断这是个回文数。

示例2:非回文数1234

  1. 调用isPal(1234),创建dup指向1234,调用isPalUtil(1234, &1234)
  2. 递归深入到isPalUtil(1, &1234):1是个位数,对比11234%10=4,返回false
  3. 这个false会直接逐层传递回去,所有上层递归都直接返回false,最终isPal(1234)返回false,判断不是回文数。

4. 总结一下

这段代码的巧妙(或者说让人一开始困惑)的点在于利用递归的回溯特性,不需要额外反转整个数字,而是通过递归的深度和副本的逐步缩小,实现原数高位和副本低位的逐一对比。本质上是用递归代替了循环的反转操作,只是因为递归的“先深入后回溯”特性,看起来有点绕而已。

内容的提问来源于stack exchange,提问作者Deepnarayan Biswas

火山引擎 最新活动