【问题标题】:Recursion function to determine the right condition out of 4递归函数确定 4 个中的正确条件
【发布时间】:2021-04-06 22:36:23
【问题描述】:

编写一个递归函数,获取一个 int n 和另一个介于 0 和 3 之间的数字 f,该函数根据以下条件返回 0 或 1:

  1. 如果 f=0,如果序列(从右到左)是升序,函数将返回 1,否则返回 0。

  2. 如果 f=1,如果序列(从右到左)是一个降序序列,函数将返回 1,否则返回 0。

  3. 如果 f=2,如果序列(从右到左)是升序到某个数字,然后是降序(直到数字的末尾),函数将返回 1,否则返回 0。

  4. 如果 f=3,如果序列(从右到左)是一个降序序列,直到某个数字,然后它是升序(直到数字的末尾),函数将返回 1,否则返回 0。

  5. 如果数字 n 有两个连续相同的数字,则对于每个 f 值,该函数将返回 0。例如,对于任何 f 值的数字 1223,该函数将返回 0。

例如:

对于 1234,0,函数返回 0

对于 4321,0,函数返回 1

对于 1234,1,函数返回 1

对于 4321,1,函数返回 0

对于 12341,2,函数返回 1

对于 412341,2,函数返回 0

对于 96589,2,函数返回 0。

对于 12341,3,函数返回 0

对于 96589,3,函数返回 1

对于任何 f 值的数字 1223,该函数将返回 0。

我写了这段代码,但 f=2 的情况总是返回 1。

    #include <stdio.h>

    int func(int n, int f)
    {

    int zero = 0;
    int* one = &zero;

    if (n < 10)
        return n;

    if((func(n / 10, f) == n % 10)
        return 0;

    switch (f)
    {
    case(0):
        if (func(n / 10, f) > n % 10)     
            return (n % 10);
        else
                return 0;

     case(1):
            if (func(n / 10, f) < n % 10)
                return (n % 10);
            else
                return 0;

    
    case(2):
            if (func(n / 10, f) > n % 10)
                return (n % 10);
            else
                return 0;
                
            if (*one == 0)
                if (func(n / 10, f) < n % 10) 
                {
                    *one++;
                    return (n % 10);
                }
            
    case(3):
        if (func(n / 10, f) < n % 10)
            return (n % 10);
        else
            return 0;
        
        if (*one == 0)
            if (func(n / 10, f) > n % 10) 
            {
                *one++;
                return (n % 10);
            }
     }
    }
    
    int main(void)
    {
        
        //int n=1234, f=0; //should return 0
        //int n=4321, f=0; //should return 1
        //int n=1234, f=1; //should return 1
        //int n=4321, f=1; //should return 0
        //int n=12341, f=2; //should return 1
        //int n=412341, f=2; //should return 0
        //int n=96589, f=2;//should return 0
        //int n=96589, f=3; //should return 1
        //int n=12341, f=3; //should return 0
    
        if (func(n , f) != 0)
            printf("1\n");
        else
            printf("0\n");
        return 0;
    }

【问题讨论】:

  • *one == 0 为假时,情况 2 或 3 会发生什么?如果它为真但以下条件为假怎么办?您必须为每个可能的路径提供一个有效的 return(如果 f 是 5 会发生什么?)。
  • 在应该返回 1 的情况下,为什么要返回 n % 10(可能是 0)?在情况 2 和 3 中,永远不会到达 if (*one == 0) 语句,因为您从它们之前的 if 语句的两个分支返回。

标签: c function recursion computer-science


【解决方案1】:

几个cmets:

  1. 我认为最好对代码进行一些结构化,使其更易于管理,例如将程序的主控件与检测升序、降序、升序_然后_降序等功能分开。
  2. 在我看来,使用 string/char * 来检查升序、降序等似乎更容易,而不是使用数学运算(通过利用 '0' - '9' 字符作为 ASCII)。

以下示例将仅提供检查升序、降序等功能。请注意,以下代码中的定义使用left-to-right 作为序列(正常情况下)。因此,对于您的right-to-left 规则,字符串在处理之前将是reversed。 使用字符串的好处是,也可以通过反转字符串来切换升序或降序的逻辑。不确定您是否有严格的规则,只允许使用整数进行处理。

样本输出:

N: 1234, F: 0, Return value: 0, Expected: 0
N: 4321, F: 0, Return value: 1, Expected: 1
N: 1234, F: 1, Return value: 1, Expected: 1
N: 4321, F: 1, Return value: 0, Expected: 0
N: 12341, F: 2, Return value: 1, Expected: 1
N: 412341, F: 2, Return value: 0, Expected: 0
N: 96589, F: 2, Return value: 0, Expected: 0
N: 96589, F: 3, Return value: 1, Expected: 1
N: 12341, F: 3, Return value: 0, Expected: 0

代码:

#include <stdio.h>
#include <string.h>

int is_ascending(char *str, int currentIdx, int prevChar);
int is_descending(char *str, int currentIdx, int prevChar);
int is_ascending_then_descending(char *str, int currentIdx, int prevChar, char whichMode);
int is_descending_then_ascending(char *str, int currentIdx, int prevChar, char whichMode);

//===================== Helper Function ======================
void inplace_reverse(char *str);
char* my_itoa(int val, int base);

int main() {
    int RetVal=-1, Expected = -1;
    int N=0, F=0;

    //Ascending
    N = 1234, F=0, Expected = 0;
    char *input1 = my_itoa(N, 10);
    inplace_reverse(input1);
    RetVal = is_ascending(input1, 0, 0);
    printf("N: %d, F: %d, Return value: %d, Expected: %d\n", N, F, RetVal, Expected);

    N = 4321, F=0, Expected = 1;
    char *input2 = my_itoa(N, 10);
    inplace_reverse(input2);
    RetVal = is_ascending(input2, 0, 0);
    printf("N: %d, F: %d, Return value: %d, Expected: %d\n", N, F, RetVal, Expected);

    //Descending
    N = 1234, F=1, Expected = 1;
    char *input3 = my_itoa(N, 10);
    inplace_reverse(input3);
    RetVal = is_descending(input3, 0, 0);
    printf("N: %d, F: %d, Return value: %d, Expected: %d\n", N, F, RetVal, Expected);

    N = 4321, F=1, Expected = 0;
    char *input4 = my_itoa(N, 10);
    inplace_reverse(input4);
    RetVal = is_descending(input4, 0, 0);
    printf("N: %d, F: %d, Return value: %d, Expected: %d\n", N, F, RetVal, Expected);

    //Ascending & then Descending
    N = 12341, F=2, Expected = 1;
    char *input5 = my_itoa(N, 10);
    inplace_reverse(input5);
    RetVal = is_ascending_then_descending(input5, 0, 0, 'a');
    printf("N: %d, F: %d, Return value: %d, Expected: %d\n", N, F, RetVal, Expected);

    N = 412341, F=2, Expected = 0;
    char *input6 = my_itoa(N, 10);
    inplace_reverse(input6);
    RetVal = is_ascending_then_descending(input6, 0, 0, 'a');
    printf("N: %d, F: %d, Return value: %d, Expected: %d\n", N, F, RetVal, Expected);

    N = 96589, F=2, Expected = 0;
    char *input7 = my_itoa(N, 10);
    inplace_reverse(input7);
    RetVal = is_ascending_then_descending(input7, 0, 0, 'a');
    printf("N: %d, F: %d, Return value: %d, Expected: %d\n", N, F, RetVal, Expected);

    //Descending & then Ascending
    N = 96589, F=3, Expected = 1;
    char *input8 = my_itoa(N, 10);
    inplace_reverse(input8);
    RetVal = is_descending_then_ascending(input8, 0, 999, 'd');
    printf("N: %d, F: %d, Return value: %d, Expected: %d\n", N, F, RetVal, Expected);

    N = 12341, F=3, Expected = 0;
    char *input9 = my_itoa(N, 10);
    inplace_reverse(input9);
    RetVal = is_descending_then_ascending(input9, 0, 999, 'd');
    printf("N: %d, F: %d, Return value: %d, Expected: %d\n", N, F, RetVal, Expected);

    return 0;
}

// ========================= Main Functionality ==========================
//Return 1 if str is Ascending from left to right; otherwise return 0
int is_ascending(char *str, int currentIdx, int prevChar) {
    char current = str[currentIdx];
    if (current == '\0') {
        return 1;
    } else {
        if (current > prevChar) {
            return is_ascending(str, currentIdx+1, current);
        } else {
            return 0;
        } //end else
    } //
}

//Return 1 if str is Descending from left to right; otherwise return 0.
//Re-use the 'is_asscending' function by reversing the string first
int is_descending(char *str, int currentIdx, int prevChar) {
    inplace_reverse(str);
    return is_ascending(str, currentIdx, prevChar);
}

//Return 1 if str is Ascending and then Descending (from left to right); otherwise return 0.
int is_ascending_then_descending(char *str, int currentIdx, int prevChar, char whichMode) {
    char current = str[currentIdx];
    if (current == '\0') {
        return 1;
    } else {
        if (current > prevChar) {
            if (whichMode == 'a') {
                return is_ascending_then_descending(str, currentIdx+1, current, whichMode);
            } else { //mode is already in Descending, but current > prevChar
                return 0;
            } //end else
        } else { //current <= prevChar
            if (whichMode == 'a') {
                return is_ascending_then_descending(str, currentIdx+1, current, 'd'); //switch to descending mode
            } else { //mode is already in Descending, current <= prevChar
                if (current < prevChar) {
                    return is_ascending_then_descending(str, currentIdx+1, current, 'd');
                } else { //current == prevChar
                    return 0;
                }
            } //end else
        } //end else
    } //
}

//Return 1 if str is Descending and then Ascending (from left to right); otherwise return 0.
int is_descending_then_ascending(char *str, int currentIdx, int prevChar, char whichMode) {
    char current = str[currentIdx];
    if (current == '\0') {
        return 1;
    } else {
        if (current < prevChar) {
            if (whichMode == 'd') {
                return is_descending_then_ascending(str, currentIdx+1, current, whichMode);
            } else { //mode is already in Ascending, but current < prevChar
                return 0;
            } //end else
        } else { //current >= prevChar
            if (whichMode == 'd') {
                return is_descending_then_ascending(str, currentIdx+1, current, 'a'); //switch to ascending mode
            } else { //mode is already in Ascending, current <= prevChar
                if (current > prevChar) {
                    return is_descending_then_ascending(str, currentIdx+1, current, 'a');
                } else { //current == prevChar
                    return 0;
                }
            } //end else
        } //end else
    } //
}

//============================= Helper Function ==============================
//reverse the given null-terminated string in place
//From: https://stackoverflow.com/questions/784417/reversing-a-string-in-c
void inplace_reverse(char *str) {
  if (str) {
    char *end = str + strlen(str) - 1;

    // swap the values in the two given variables
    // XXX: fails when a and b refer to same memory location
#   define XOR_SWAP(a,b) do\
    {\
      a ^= b;\
      b ^= a;\
      a ^= b;\
    } while (0)

    // walk inwards from both ends of the string,
    // swapping until we get to the middle
    while (str < end)
    {
      XOR_SWAP(*str, *end);
      str++;
      end--;
    }
#   undef XOR_SWAP
  }
}

char* my_itoa(int val, int base) {
    static char buf[32] = {0};
    int i = 30;
    for(; val && i ; --i, val /= base)
        buf[i] = "0123456789abcdef"[val % base];

    return &buf[i+1];
}

【讨论】:

    【解决方案2】:

    您的代码中有几个问题:您的 if (*one == 0) 语句从未被调用过。此外,*one++; 并没有按照你的想法做,我将其替换为全局变量(它可以替换为静态变量,但我确实喜欢这样从 main 中重置变量)。

    // Retain inverted status across recursive calls
    int inverted = 0;
    
    int func(int n, int f)
    {
        if (n < 10) return n;
        int p = func(n / 10, f);  //Recursive call
        
        if (p == n % 10) return 0;
        switch (f)
        {
            case(0):
                if (p > n % 10) return (n % 10);
                else return 0;
            case(1):
                if (p && p < n % 10) return (n % 10);
                else return 0;
            case(2):
                // First clause not called if in inverted segment or if p == 0
                if (inverted == 0 && p && p < n % 10) return (n % 10);
                else {
                    if (inverted == 0) inverted = 1;
                    // Second segment only called if inverted is true 
                    if (inverted && p > n % 10) { return (n % 10);  }
                    else return 0;
                }
            case(3):
                // First clause not called if in inverted segment
                if (inverted == 0 && p > n % 10) return (n % 10);
                else {
                    if (inverted == 0) inverted = 1;
                    // Second segment only called if inverted is true and if p != 0 
                    if (inverted && p && p < n % 10) return (n % 10);
                    else return 0;
                }
         }
    }
    int main(void)
    {
        int cases[9][2] = {
            {1234, 0}, 
            {4321, 0}, 
            {1234, 1}, 
            {4321, 1}, 
            {12341, 2}, 
            {412341, 2}, 
            {96589, 2}, 
            {96589, 3}, 
            {12341, 3} };
        for (char i=0; i<9; i++) {
            inverted = 0;
            printf("%d ", func(cases[i][0] , cases[i][1]) != 0);
        }
        printf("\n");
        return 0;
    }
    

    输出:

    0 1 1 0 1 0 0 1 0 
    

    请注意,操作顺序与您想象的不完全一样:由于递归,该过程向前而不是向后(最深的调用首先发生)。

    【讨论】:

      【解决方案3】:

      您的代码有几个问题:

      • 该函数应该返回 0(假)或 1(真),但您似乎在该递归步骤中将真值情况的返回值用于实际数字。如果所有数字都是正数,那可能没问题,因为除了 0 之外的任何值都被认为是“真”,但我认为 210 对于案例 0 应该返回真。

      • 在情况 2 和 3 中,您还有一个附加条件:

            if (func(n / 10, f) > n % 10)
                return (n % 10);
            else
                return 0;
        
            if (*one == 0) ...  // dead code!
        

        无法达到该条件下的if,因为您之前已经返回了 0 或 n % 10。 (正如 Bob 告诉你的,如果使用这些代码,你也不会捕捉到所有可能的路径。)

      • 您的指针one 不会给您带来任何好处,因为它总是绑定到同一个局部变量zero。所有出现的one 都是取消引用(*one),您可以将它们全部替换为zero。这意味着,如果您的条件 (*one == 0) 实际上是可访问的,那么它总是正确的。

        我猜你的意图是有一个在其他回收步骤中可见的标志,但它不是那样工作的。为此,您应该传递一个指向某个值的指针,以便您可以在所有递归步骤中通过它访问相同的数据。)

      好的,让我们重写这个函数,让它总是返回 0 或 1。首先,为当前和下一个数字设置一些局部变量并捕获基本情况:

      int func(int n, int f)
      {
          int p = n % 10;
          int q = (n / 10) % 10;
      
          if (n < 10) return (f < 2);
          if (p == q) return 0;
      
          // ...
          
          return 0;
      }
      

      现在让我们考虑前两种情况:如果两位数乱序,则可以立即返回 0。否则,用剩余的数字递归。你可以用一条 return 语句简洁地写出来,因为循环 &amp;&amp; 确保当 p &lt; qp &gt; q 为 false 时你永远不会递归:

          switch (f) {
          case 0:     return (p < q && func(n / 10, f));
          case 1:     return (p > q && func(n / 10, f));
          // ...
          }
      

      现在其他两种情况都有一个高峰。如上所述测试您的条件。当他们失败时,你已经达到了顶峰,其余的数字一定会减少。幸运的是,你已经有一个函数来检查,nemale case 0 和 1。所以在初始条件成立时测试和递归,否则对剩余的数字调用适当的函数:

          switch (f) {
          case 0:     return (p < q && func(n / 10, f));
          case 1:     return (p > q && func(n / 10, f));
          case 2:     return (p < q && func(n / 10, f)) || func(n, 1);
          case 3:     return (p > q && func(n / 10, f)) || func(n, 0);
          }
      

      此实现将满足情况 0 或 1 的严格单调数视为情况 2 和 3 的特殊情况,其中峰值或谷值位于末尾。

      请注意,由于 switch 中的所有测试都不允许相等,因此 if (p == q) return 0; 是多余的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-11
        • 1970-01-01
        • 2014-12-21
        • 2010-10-24
        • 1970-01-01
        • 2023-01-28
        相关资源
        最近更新 更多