(C) 仅接受1个参数的递归strcpy()

(C) 仅接受1个参数的递归strcpy(),c,arrays,string,recursion,C,Arrays,String,Recursion,让我一开始就说清楚,这不是一个骗局,我会解释怎么做。 因此,我要求自己编写一个函数,模仿strcpy,但有两个条件: 它需要是递归的 它必须采用单个参数(即原始字符串) 函数应返回指向新复制字符串的指针。这就是我到目前为止所尝试的: #include <stdio.h> #include <string.h> #include <stdlib.h> char * my_strcpy(char *original); int main(void) { c

让我一开始就说清楚,这不是一个骗局,我会解释怎么做。 因此,我要求自己编写一个函数,模仿strcpy,但有两个条件:

  • 它需要是递归的
  • 它必须采用单个参数(即原始字符串)
  • 函数应返回指向新复制字符串的指针。这就是我到目前为止所尝试的:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    char * my_strcpy(char *original);
    
    int main(void) {
      char *string = my_strcpy("alpine");
      printf("string = <%s>\n", string);
      return 0;
    }
    
    char * my_strcpy(char *original){
      char *string = (char *)malloc(10);
      if(*original == '\0') {
        return string;
      }
      *string++ = *original;
      my_strcpy(original + 1);
    }
    
    #包括
    #包括
    #包括
    char*我的简历(char*原件);
    内部主(空){
    char*string=my_strcpy(“阿尔卑斯”);
    printf(“string=\n”,string);
    返回0;
    }
    char*my_strcpy(char*original){
    char*string=(char*)malloc(10);
    如果(*原始=='\0'){
    返回字符串;
    }
    *字符串+=*原始;
    我的简历(原件+1);
    }
    
    问题有点明显,
    string
    在每次调用
    my_strcpy()
    时都会得到
    malloc
    -ed。我能想到的解决方案之一是仅在第一次调用函数时为
    string
    分配内存。因为我只允许有一个参数,所以我能想到的唯一一件事就是检查调用堆栈,但我不知道这是否是允许的,这确实感觉像是作弊。
    这个问题有一个合乎逻辑的解决方案吗?

    您把它写成了tail recursive,但我认为如果不使函数不可重入,您唯一的选择就是使函数head递归,并在递归调用的返回值上重复调用realloc来扩展它,然后添加一个字符。这与调用strlen进行分配有着相同的问题:它在每次递归调用中对输入字符串的长度进行线性处理,结果是隐式的n平方算法(0.5*n*(n+1))。您可以通过使摊销时间复杂度更好来改进它,将字符串扩展一个因子,并且仅在现有缓冲区已满时才进行增长,但仍然不是很好

    这项任务不使用递归有一个原因(您可能知道):堆栈深度将等于输入字符串长度,并且推送整个堆栈帧和复制的每个字符的调用指令都会带来大量开销。即使如此,如果你真的要递归地做,你也不会用一个参数递归地做:你要做一个单参数函数,声明一些局部变量,并用多个参数调用一个递归函数

    即使使用realloc技巧,也很难或不可能在运行时计算原始文件中的字符数,以便可以适当地调用realloc,记住其他stdlib“str*”函数是禁止使用的,因为它们可能会使整个函数n平方,我认为我们正试图避免这种情况


    可以使用一些丑陋的技巧,比如验证字符串是否与指针一样长,以及用memcpy替换指针的前几个字符,这使得递归的基本情况更加复杂,但是,嗯,真糟糕。

    你没有说我们不能使用strcat。 所以这里有一个合乎逻辑(虽然有些无用)的答案,使用递归只需切掉最后一个字符并再次添加它

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    char * my_strcpy(char *original);
    
    int main(void) {
      char *string = my_strcpy("alpine");
      printf("string = <%s>\n", string);
      return 0;
    }
    
    char * my_strcpy(char *original){
      if(*original == '\0') {
        return original;
      }
      int len = strlen(original);
      char *string = (char *)malloc(len+1);
      char *result = (char *)malloc(len+1);
      string[0] = result[0] = '\0';
    
      strcat (string, original);
      len--;
      char store[2] = {string[len] , '\0'}; // save last char
      string[len] = '\0';                   // cut it off
      strcat (result, my_strcpy(string));
    
      strcat (result, store);               // add it back
      return result;
    }
    
    #包括
    #包括
    #包括
    char*我的简历(char*原件);
    内部主(空){
    char*string=my_strcpy(“阿尔卑斯”);
    printf(“string=\n”,string);
    返回0;
    }
    char*my_strcpy(char*original){
    如果(*原始=='\0'){
    归还原件;
    }
    int len=strlen(原件);
    char*string=(char*)malloc(len+1);
    char*result=(char*)malloc(len+1);
    字符串[0]=结果[0]='\0';
    strcat(字符串,原件);
    蓝--;
    字符存储[2]={string[len],'\0'};//保存最后一个字符
    字符串[len]='\0';//将其切断
    strcat(结果,我的strcpy(字符串));
    strcat(结果,存储);//将其添加回
    返回结果;
    }
    
    递归是一种分析问题的技术。也就是说,您从问题开始,考虑解决方案的递归结构可能是什么。你不会从一个递归结构开始,然后试图随意地把你的问题拖入其中

    换句话说,练习递归分析是很好的,但是您自己设置的任务——强制解决方案采用单参数函数的形式——并不是实现这一点的方法。如果您开始考虑全局变量或静态变量,或者通过打破调用堆栈来提取运行时上下文,您会得到一个非常好的提示,即您尚未找到适当的递归分析

    这并不是说你的问题没有一个优雅的递归解决方案。有一个,但在我们开始之前,我们可能想抽象出问题的细节,以便提供一些动机

    显然,如果我们的内存中已经有一个连续的数据结构,那么创建一个连续的副本并不具有挑战性。如果我们不知道它有多大,我们可以做两次遍历:一次查找它的大小,然后分配所需的内存,另一次进行复制。这两个任务都是简单循环,这是递归的一种形式

    递归解决方案的本质是考虑如何从一个问题过渡到一个(稍微)简单或更小的问题。或者,更常见的是,少量较小或更简单的问题

    这就是最经典的递归问题之一的本质:对数字序列进行排序。基本结构:将序列分成两个大致相等的部分;对每个部分(递归步骤)进行排序,并将结果放回一起,以便对组合进行排序。这一基本轮廓至少有两种有趣(而且非常不同)的表现形式:

    • 将序列任意划分为两个几乎相等的部分,或者将替换元素放在替换部分,或者将前半部分放在一部分,其余部分放在另一部分。(如果我们事先不知道序列有多大,第一个会很好地工作。)将排序的部分放在一起
      Length(seq):
          If seq is empty, return 0.
          Else, return 1 + Length(Tail(seq))
      
      Copy(destination, seq):
          If seq is not empty:
              Put Head(seq) into the location destination
              Call Copy (destination+1, Tail(seq))
      
      Copy(seq, length):
          If seq is not empty:
              Set item to its first element (that is, Head(seq))
              Set destination to Copy(Tail(seq), length + 1)
              Store item at location destination - 1
              Return destination - 1
          Otherwise: (seq is empty)
              Set destination to Allocate(length)
              # (see important note below)
              Return destination + length
      
      Strdup(seq):
          Return Copy (seq, 0)