C递归函数查找2个长数字之间的公共数字

jtw3ybtb  于 2022-12-26  发布在  其他
关注(0)|答案(4)|浏览(121)

我给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);

}
e5nqia27

e5nqia271#

因此,我的理解是,任务是确定两个数字是否至少共享一个数字。约束条件是:

  • 从右到左扫描。
  • 在(21,12)的情况下,返回1,而不是2。(根据您的注解。)
  • 解应该是递归函数。

正确的O。所以,诀窍是比较第一个数和第二个数的每一位,有很多方法可以优化这个过程,但我们还是坚持使用O(n²)的蛮力方法。
给定123456

  • 比较36
  • 比较35
  • 比较34
  • 比较26
  • 比较25
  • 比较24
  • 比较16
  • 比较15
  • 比较14

希望您能看到这是一个嵌套循环:

for each digit in N1:
  for each digit in N2:
    match?

方便的是,获取最右边(“最低有效”)的数字值是一个简单的余数运算:

int digit = number % 10;

然后,我们修改数字,使用整数除法将所有数字位向下移动一位:

number = number / 10;

我们希望使用 recursion(而不是像whilefor这样的 looping 结构),因此我们需要将这些循环中的每一个转换为递归函数调用。

基本递归

递归函数的工作原理是有一个或多个终止条件。如果终止条件失败,它就用更新的参数调用自己。
举个简单的例子,我们可以做一个计数循环:

void print_START_to_STOP( int start, int stop )
{
  for (;  start < stop;  start = start+1)
    printf( "%d\n", start );
}

并使其递归:

void print_START_to_STOP( int start, int stop )
{
  if (!(start < stop)) return;           // termination condition
  printf( "%d\n", start );               // (loop body)
  print_START_to_STOP( start+1, stop );  // invoke next iteration
}

花几分钟时间让自己相信这两个函数是等价的,一个使用显式循环,一个使用递归。

内环

让我们从简单的开始,为内部循环编写一个递归函数:

bool isDigitInNumber( int digit, unsigned long long number )
{
  ...
}

由于递归需要至少一个终止条件,所以首先要检查number中是否还有数字要与参数digit进行比较,如果没有,则不匹配。

if (number == 0) return false;

接下来要做的是检查number的最低有效位是否与digit匹配,如果匹配,我们还有另一个终止条件:

if ((number % 10) == digit) return true;

否则,我们需要检查number中剩余的数字,这就是我们调用递归的地方:

return isDigitInNumber( digit, number/10 );

零?
您可能会问:“如果digit0,而number中有一个0,该怎么办?”
给予看,number == 201的情况下,它能工作吗?
“好吧,那么如果digit0number也是0呢?”
这是一个好问题。findCommonDigit( 0, 0 )应该返回0还是-1
(很可能你的教授期望findCommonDigit( 0, 0 )返回-1。我个人认为如果它返回0会更正确。唯一 * 不应该 * 返回0的是如果其中一个数字没有嵌入零,比如123456,以及0123。)
我们过一会儿再来谈这个。

反思

让我们想想我们做了什么来简化我们的问题:

  • isDigitInNumber( 3, 456 )
  • isDigitInNumber( 2, 456 )
  • x1米50英寸

如果其中任何一个为真,我们就可以返回匹配的数字,否则返回-1
您还应该注意到,还有一个外部循环需要征服。

外环

对于外层循环,我们可以创建一个新的递归函数,它利用了上一个递归函数。
花点时间想想这个递归循环的对象是什么:正如最后一个函数在N2上循环并且通过使用N2上的余数来获得要比较的数位,* 这个 * 递归函数在N1上循环并且通过使用N1上的余数来获得要比较的数位:

int digit_to_compare = n1 % 10;

同样,我们需要一些终止条件和递归。
我们什么时候停止并返回-1
(在循环N1的所有数字且未使用isDigitInNumber()找到匹配项之后。)
所以......当N1为0时,我们就完成了。没有匹配。返回-1

if (n1 == 0) return -1;

否则,我们需要尝试将要比较的位数与N2中的所有位数进行匹配:

if (isDigitInNumber( digit_to_compare, n2 )) return digit_to_compare;

最后,数字不匹配,我们需要递归N1中的下一个数字:

return findCommonDigit( ... );

你基本上完成了。

归零还原

现在回到那个关于零的棘手问题。
请记住,内循环算法 * 不能 * 检查0与0匹配的可能性,但没关系,它不是检查的正确位置。
正确的地方是在你做任何循环之前,就在你的算法开始的时候,检查一下N1是否== N2,如果是的话,那么两个数字的最右边的数字是相同的,即使两个数字都是零。

int findCommonDigit( ... )
{
  if (n1 == n2) return (n1 % 10);  // return the matching digit, which might be 0
  return findCommonDigit_Impl( n1, n2 );
}

当然,您必须将最后一个findCommonDigit()函数重命名为findCommonDigit_Impl()

这么多功能!什么是双曲棍球?!

为了处理零的特殊情况,我们不得不在混合中添加 * 另一个 * 函数。
这是不可避免的,否则我们无法区分findCommonDigit( 123, 0 )findCommonDigit( 0, 0 )之间的区别,并且( 123, 0 )( 0, 123 )之间的结果也不同。
不管怎样,我们现在有三个功能:

bool isDigitInNumber( int digit, unsigned long long number );
int findCommonDigit_Impl( unsigned long long n1, unsigned long long n2 );
int findCommonDigit( unsigned long long n1, unsigned long long n2 );

最后一个是由用户调用的:

int main(void)
{
  printf( "%d\n", findCommonDigit( 1234, 2 ) );

另外两个是递归函数,用户不调用它们。

完全有可能将所有这些组合成一个 * 单个 * 递归函数。

但是不要浪费时间。这样做会增加复杂性,而且你必须添加额外的形式化参数来处理它。(或者使用全局变量或其他一些“不要这么做”的方法)。
有有用的辅助函数是完全可以的,而且在处理递归时,这种多函数结构实际上是很常见的。
希望这不是太冗长,也不是太容易给出答案。这里花的大部分时间是帮助你思考设计递归算法的方法,这样当你将来不得不再做一次的时候,你就不会完全迷路了。(递归是很难的!)
AAAANNNND,希望你的同学也能得到它,但是不要产生一个你代码的精确副本,哈哈。教授们希望你学习,而不是在互联网上作弊,我已经让这个相当容易得出这样的结论,唉。

附言

顺便说一句,在递归函数中使用循环可能不会有什么问题:N2的数字的显式内部循环和N1上的外部循环的递归。IDK如果这是可接受的。这个答案假设不允许循环。

eufgjt7s

eufgjt7s2#

您可以使用基于循环的函数,如下所示:
int findCommonDigit(unsigned long long n1, unsigned long long n2) {
    int digitN1[11] = {0}, digitN2[11] = {0};
    while(n1) {
        int x = n1 % 10;
        n1 /= 10;
        digitN1[x] = 1;
    }
    while(n2) {
        int x = n2 % 10;
        n2 /= 10;
        digitN2[x] = 1;
    }
    for(int i=0; i<10; i++) {
        if(digitN1[i] && digitN2[i]) {
            return i;
        }
    }
    return -1;
}

这里digitN1digitN2是两个标志数组,分别用来存储数字n1n2是否设置了数字[0..9],然后我们使用一个for循环来检查这两个数字上是否有任何数字,如果没有找到共同的数字,则返回-1。

83qze16e

83qze16e3#

你可以写一个while循环,直到main函数中的“n2〉0”。对于每个数字,你将调用“findCommonDigit”函数并检查返回值是否为-1。如果你得到一个不是-1的数字,则打印它并中断while循环。在while循环之后,你可以写printf(“-1”)。为此,你必须将return findCommonDigit(n1 / 10, n2 / 10);更改为return findCommonDigit(n1 / 10, n2);
或者你可以把你的函数改成这样,

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;
    }
    n1 /= 10;
    return findCommonDigit(n1, n2);
    n2 /= 10;
    return findCommonDigit(n1, n2);
}
w3nuxt5m

w3nuxt5m4#

这是我根据你和我的测试案例得出的结论。
两倍的递归两倍的乐趣!

#include <stdio.h>

#define DEBUG_FUNCTION 0

int compareN1Digits(unsigned long long n1, unsigned long long n2)
{
    int n1RightDigit = n1 % 10;
    if(DEBUG_FUNCTION) printf("    n1 right most digit : %d\n", n1RightDigit);
    
    int result = -1;
    if (n1RightDigit != n2)
    {
        if(DEBUG_FUNCTION) printf("Not a match.\n");
        if (n1 > 10)
        {
            result = compareN1Digits(n1 / 10, n2);
        }
    }
    else
    {
        result = n1RightDigit;
    }
    
    return result;
}

int findCommonDigit(unsigned long long n1, unsigned long long n2)
{
    int n2RightDigit = n2 % 10;
    if(DEBUG_FUNCTION) printf("n2 right most digit : %d\n", n2RightDigit);
    
    int result = -1;

    result = compareN1Digits(n1, n2 % 10);

    if (result == -1)
    {
        if (n2 >= 10)
        {
            result = findCommonDigit(n1, n2 / 10);
        }
    }
    
    return result;
}

void RunTestCase(unsigned long long n1, unsigned long long n2, int expectedResult)
{
    int result;
    
    result = findCommonDigit(n1, n2);
    if (result == expectedResult)
    {
        printf("Corrent Result\n\n");
    }
    else
    {
        printf("Result NOT corrent!\n\n");
    }
}
    
int main()
{
    // Test Case 1
    // 22222446, 113355578889 => function will give back -1
    RunTestCase(22222446, 113355578889, -1);

    // Test Case 2
    // 13259438, 2 => function will give back 2
    RunTestCase(13259438, 2, 2);

    // Test Case 3
    // 112233445, 112233445 => function will give back 5.
    RunTestCase(112233445, 112233445, 5);
    
    // Test Case 4
    // 2, 13259438 => function will give back 2
    RunTestCase(2, 13259438, 2);

    // Test Case 5
    // 12034, 567809 => function will give back 0
    RunTestCase(12034, 567809, 0);
    
    // Test Case 6
    // 1, 1 => function will give back 1
    RunTestCase(1, 1, 1);
    
    // Test Case 7
    // 0, 0 => function will give back 0
    RunTestCase(0, 0, 0);

    return 0;
}

相关问题