我给hw一个任务,让它做一个递归函数,检查2个无符号的long long数,如果有,检查公共位,它将打印公共位(它从右到左开始检查-当发现匹配时,我们停止并打印该位),如果没有相同的位,它将返回-1
示例:
22222446, 113355578889 => function will give back -1
13259438, 2 => function will give back 2
112233445, 112233445 => function will give back 5.
问题是我可以让它在第一个和最后一个例子中工作,但是我在处理第二个例子时遇到了麻烦。任何提示都将不胜感激:)
int findCommonDigit(unsigned long long n1, unsigned long long n2) {
int temp1 = 0, temp2 = 0;
if (n1 == 0 || n2 == 0)
return -1;
temp1 = n1 % 10;
temp2 = n2 % 10;
if (temp1 == temp2)
return temp1;
return findCommonDigit(n1 / 10, n2 / 10);
}
4条答案
按热度按时间e5nqia271#
因此,我的理解是,任务是确定两个数字是否至少共享一个数字。约束条件是:
正确的O。所以,诀窍是比较第一个数和第二个数的每一位,有很多方法可以优化这个过程,但我们还是坚持使用O(n²)的蛮力方法。
给定
123
和456
:3
与6
3
与5
3
与4
2
与6
2
与5
2
与4
1
与6
1
与5
1
与4
希望您能看到这是一个嵌套循环:
方便的是,获取最右边(“最低有效”)的数字值是一个简单的余数运算:
然后,我们修改数字,使用整数除法将所有数字位向下移动一位:
我们希望使用 recursion(而不是像
while
或for
这样的 looping 结构),因此我们需要将这些循环中的每一个转换为递归函数调用。基本递归
递归函数的工作原理是有一个或多个终止条件。如果终止条件失败,它就用更新的参数调用自己。
举个简单的例子,我们可以做一个计数循环:
并使其递归:
花几分钟时间让自己相信这两个函数是等价的,一个使用显式循环,一个使用递归。
内环
让我们从简单的开始,为内部循环编写一个递归函数:
由于递归需要至少一个终止条件,所以首先要检查
number
中是否还有数字要与参数digit
进行比较,如果没有,则不匹配。接下来要做的是检查
number
的最低有效位是否与digit
匹配,如果匹配,我们还有另一个终止条件:否则,我们需要检查
number
中剩余的数字,这就是我们调用递归的地方:零?
您可能会问:“如果
digit
是0
,而number
中有一个0
,该怎么办?”给予看,
number
==201
的情况下,它能工作吗?“好吧,那么如果
digit
是0
,number
也是0
呢?”这是一个好问题。
findCommonDigit( 0, 0 )
应该返回0
还是-1
?(很可能你的教授期望
findCommonDigit( 0, 0 )
返回-1
。我个人认为如果它返回0
会更正确。唯一 * 不应该 * 返回0
的是如果其中一个数字没有嵌入零,比如123
和456
,以及0
和123
。)我们过一会儿再来谈这个。
反思
让我们想想我们做了什么来简化我们的问题:
isDigitInNumber( 3, 456 )
isDigitInNumber( 2, 456 )
如果其中任何一个为真,我们就可以返回匹配的数字,否则返回
-1
。您还应该注意到,还有一个外部循环需要征服。
外环
对于外层循环,我们可以创建一个新的递归函数,它利用了上一个递归函数。
花点时间想想这个递归循环的对象是什么:正如最后一个函数在
N2
上循环并且通过使用N2
上的余数来获得要比较的数位,* 这个 * 递归函数在N1
上循环并且通过使用N1
上的余数来获得要比较的数位:同样,我们需要一些终止条件和递归。
我们什么时候停止并返回
-1
?(在循环
N1
的所有数字且未使用isDigitInNumber()
找到匹配项之后。)所以......当
N1
为0时,我们就完成了。没有匹配。返回-1
。否则,我们需要尝试将要比较的位数与
N2
中的所有位数进行匹配:最后,数字不匹配,我们需要递归
N1
中的下一个数字:你基本上完成了。
归零还原
现在回到那个关于零的棘手问题。
请记住,内循环算法 * 不能 * 检查0与0匹配的可能性,但没关系,它不是检查的正确位置。
正确的地方是在你做任何循环之前,就在你的算法开始的时候,检查一下
N1
是否==N2
,如果是的话,那么两个数字的最右边的数字是相同的,即使两个数字都是零。当然,您必须将最后一个
findCommonDigit()
函数重命名为findCommonDigit_Impl()
。这么多功能!什么是双曲棍球?!
为了处理零的特殊情况,我们不得不在混合中添加 * 另一个 * 函数。
这是不可避免的,否则我们无法区分
findCommonDigit( 123, 0 )
和findCommonDigit( 0, 0 )
之间的区别,并且( 123, 0 )
和( 0, 123 )
之间的结果也不同。不管怎样,我们现在有三个功能:
最后一个是由用户调用的:
另外两个是递归函数,用户不调用它们。
完全有可能将所有这些组合成一个 * 单个 * 递归函数。
但是不要浪费时间。这样做会增加复杂性,而且你必须添加额外的形式化参数来处理它。(或者使用全局变量或其他一些“不要这么做”的方法)。
有有用的辅助函数是完全可以的,而且在处理递归时,这种多函数结构实际上是很常见的。
希望这不是太冗长,也不是太容易给出答案。这里花的大部分时间是帮助你思考设计递归算法的方法,这样当你将来不得不再做一次的时候,你就不会完全迷路了。(递归是很难的!)
AAAANNNND,希望你的同学也能得到它,但是不要产生一个你代码的精确副本,哈哈。教授们希望你学习,而不是在互联网上作弊,我已经让这个相当容易得出这样的结论,唉。
附言
顺便说一句,在递归函数中使用循环可能不会有什么问题:
N2
的数字的显式内部循环和N1
上的外部循环的递归。IDK如果这是可接受的。这个答案假设不允许循环。eufgjt7s2#
您可以使用基于循环的函数,如下所示:
这里
digitN1
和digitN2
是两个标志数组,分别用来存储数字n1
和n2
是否设置了数字[0..9],然后我们使用一个for循环来检查这两个数字上是否有任何数字,如果没有找到共同的数字,则返回-1。83qze16e3#
你可以写一个while循环,直到main函数中的“n2〉0”。对于每个数字,你将调用“findCommonDigit”函数并检查返回值是否为-1。如果你得到一个不是-1的数字,则打印它并中断while循环。在while循环之后,你可以写printf(“-1”)。为此,你必须将
return findCommonDigit(n1 / 10, n2 / 10);
更改为return findCommonDigit(n1 / 10, n2);
或者你可以把你的函数改成这样,
w3nuxt5m4#
这是我根据你和我的测试案例得出的结论。
两倍的递归两倍的乐趣!