确定 4 个正确条件的递归函数

问题描述

编写一个递归函数,得到一个 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。例如,对于数字 1223,对于任何 f 值,函数将返回 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;
    }

解决方法

您的代码有几个问题:

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

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

    dfTest %>%
      mutate(Height = rm_between(Values,"Height : ","cm",extract = TRUE))
      ID                     Values Height
    1  1                          A     NA
    2  2                          B     NA
    3  3 Height : 13cm Width : 14cm     13
    

    无法达到该条件下的 if (func(n / 10,f) > n % 10) return (n % 10); else return 0; if (*one == 0) ... // dead code! ,因为您之前已经返回了 0 或 if。 (正如鲍勃告诉您的那样,如果这些条件,您也不会捕获所有可能的路径。)

  • 你的指针 n % 10 不会给你任何东西,因为它总是绑定到同一个局部变量 one。所有出现的 zero 都是取消引用 (one),您可以将它们全部替换为 *one。这意味着,如果您的条件 zero 实际上是可达的,则它始终为真。

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

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

(*one == 0)

现在我们考虑前两种情况:如果两位数字乱序,可以立即返回0。否则,用剩余的数字递归。您可以使用单个 return 语句简洁地编写此代码,因为短路 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; } 确保您永远不会在 &&p < q 为 false 时递归:

p > q

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

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

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

请注意,由于 switch (f) { case 0: 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,0); } 中的所有测试都不允许相等,因此 switch 是多余的。

,

您的代码中有几个问题:您的 if (*one == 0) 语句从未被调用。此外,*one++; 并没有像你想象的那样做,我用一个全局变量代替了它(它可以用一个静态变量代替,但我这样做是为了从主重置变量)。

// 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,{1234,1},{12341,2},{412341,{96589,3},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 

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

,

一些评论:

  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,Return value: 1,Expected: 1
N: 1234,F: 1,Expected: 1
N: 4321,Expected: 0
N: 12341,F: 2,Expected: 1
N: 412341,Expected: 0
N: 96589,F: 3,Expected: 1
N: 12341,Expected: 0

代码:

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

int is_ascending(char *str,int currentIdx,int prevChar);
int is_descending(char *str,int prevChar);
int is_ascending_then_descending(char *str,int prevChar,char whichMode);
int is_descending_then_ascending(char *str,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);
    printf("N: %d,F: %d,Return value: %d,Expected: %d\n",N,F,RetVal,Expected);

    N = 4321,Expected = 1;
    char *input2 = my_itoa(N,10);
    inplace_reverse(input2);
    RetVal = is_ascending(input2,Expected);

    //Descending
    N = 1234,F=1,Expected = 1;
    char *input3 = my_itoa(N,10);
    inplace_reverse(input3);
    RetVal = is_descending(input3,Expected = 0;
    char *input4 = my_itoa(N,10);
    inplace_reverse(input4);
    RetVal = is_descending(input4,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,'a');
    printf("N: %d,Expected);

    N = 412341,Expected = 0;
    char *input6 = my_itoa(N,10);
    inplace_reverse(input6);
    RetVal = is_ascending_then_descending(input6,Expected);

    N = 96589,Expected = 0;
    char *input7 = my_itoa(N,10);
    inplace_reverse(input7);
    RetVal = is_ascending_then_descending(input7,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,999,'d');
    printf("N: %d,Expected);

    N = 12341,Expected = 0;
    char *input9 = my_itoa(N,10);
    inplace_reverse(input9);
    RetVal = is_descending_then_ascending(input9,Expected);

    return 0;
}

// ========================= Main Functionality ==========================
//Return 1 if str is Ascending from left to right; otherwise return 0
int is_ascending(char *str,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 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,char whichMode) {
    char current = str[currentIdx];
    if (current == '\0') {
        return 1;
    } else {
        if (current > prevChar) {
            if (whichMode == 'a') {
                return is_ascending_then_descending(str,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,'d'); //switch to descending mode
            } else { //mode is already in Descending,current <= prevChar
                if (current < prevChar) {
                    return is_ascending_then_descending(str,'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,char whichMode) {
    char current = str[currentIdx];
    if (current == '\0') {
        return 1;
    } else {
        if (current < prevChar) {
            if (whichMode == 'd') {
                return is_descending_then_ascending(str,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,'a'); //switch to ascending mode
            } else { //mode is already in Ascending,current <= prevChar
                if (current > prevChar) {
                    return is_descending_then_ascending(str,'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];
}