繁体   English   中英

递归连接以null结尾的字符串

[英]Concatenate null-terminated strings recursively

我发明了面试问题,需要分析C ++代码,这些代码对指针和递归的操作很简单。 我试图以递归方式编写strcat()

size_t mystrlen( const char* str )
{
    if( *str == 0 ) {
        return 0;
    }
    return 1 + mystrlen( str + 1 );
}

void mystrcpy( char* to, const char* from )
{
    if( ( *to = *from ) == 0 ) {
        return;
    }
    mystrcpy( to + 1, from + 1 );
}

void mystrcat( char* to, const char* from )
{
    mystrcpy( to + mystrlen( to ), from );
}

我在这里不喜欢的是我有三个函数,而我的strcat()不是很递归的-它只调用另外两个函数。 有没有一种方法可以重写它,从而减少函数的数量,从而增加递归的使用,但又不牺牲太多的代码简洁性?

这是我的示例(优点是只有一个递归调用):

char * mystrcat(char *dest, const char *src){
    if(*dest == 0){
        if(*src == 0)   // end of recursion cond
            return dest;
        *dest = *src++; // actual copy
        dest[1]=0;      // zero out dest buf
    }
    mystrcat(dest+1,src); // advance one char
    return dest;
}

这是粗略的测试代码:

main(){
  char c[]={'a',0,'b','c',0};
  //char c[]={0,'b','c',0};
  mystrcat(c,"xy");
  //mystrcat(c,"");
  puts(c);
}

好吧,尽管试图访问strcat()尽可能“递归”,但我并没有真正意义。 strcpy()只是一个strlen()+ strcat(),实现应该反映这一点。 如果候选人给出了您所概述的解决方案,我将不胜高兴-他/她表明他/她理解1.递归和2.使用子例程实现更复杂的功能。

怎么样:

void mystrcat( char* to, const char* from )
{
    if (*to == '\0')
        mystrcpy( to, from );
    else
        mystrcat( to + 1, from );     
 }

在一个函数中,您可以编写:

// pre: to is a string that is zero-initiallized (all chars are '\0')
void mystrcat( char* to, const char* from )
{
   if ( *to ) mystrcat( to+1, from );
   else {
      if (*to = *from) mystrcat( to+1, from+1 );
   }
}

请注意,这是故意丑陋的,它在两个分支中用于目的,它在第一个分支中前进to指针,在另一个分支中复制值。 if条件也是有意丑陋的,因为通常情况下,您会在条件中将单个=标记为错字,但要坚持要求它已经启动,运行并经过测试,以便他们必须自己找出含义。

您可能考虑的另一件事是添加一个return语句,例如:

char* mystrcat2( char* to, const char* from )
{
   if ( *to ) return mystrcat2( to+1, from );
   else {
      if ( *to = *from ) return mystrcat2( to+1, from+1 );
      else return to;
   }
}

现在,受访者的第二个任务是解释在两个字符串上调用时函数的结果,并且您可以在单个字符串串联和多个串联方面讨论该strcat版本的效率与原始版本的比较。请参阅Joel Spolsky 撰写的这篇文章 ,其中主要讨论了Scheimel绘制算法。 您可以使用strcatmystrcat来要求该功能的实现:

// concatenates a set of strings into a buffer
//
// pre:  to has memory enough to hold all the contents
//       from[i] is a valid C string for all i smaller than n
void mymultiplecat( char* to, const char* from[], int n )
{
   for ( int i = 0; i < n; ++i ) {
      to = mystrcat( to, from[i] );
   }
}

我想到了另一个答案!

void
mystrcat(char* to, const char* from)
{
    if (*to)
    {
        // keep looking for end of to
        mystrcat(to+1, from);
        return;
    }

    if (*from)
    {
        // make sure we get past first if
        to[1] = '\0';
        // call recursively until end of from found
        mystrcat(to+1, from+1);
        // after call, copy chars on way out
        *to = *from;
    }
}

没有额外的参数来保持状态,它仍然是O(n)。

不过,我不确定在求职面试的压力下我能否提出这个建议。

曾经有一个采访问题,我必须编写一个简单的,递归的BSP树遍历,这是整个程序中真正的空白功能。

我建议,与发明一个C ++递归无法解决的问题相比,类似的方法可以更好地衡量某人对递归的掌握程度。 另外,对于入门级申请人来说,当您运行该程序并且他们的代码完美无缺地运行时,这是令人兴奋的。

如果您坚持使用strcat,我认为如果任务是递归连接从链接列表中获取的任意数量的字符串,可能会更有趣。 即使数据复制和字符串长度操作不是递归的,也可以遍历链接列表,并且我认为它会更有趣,因为您实际上可以在向下和向上进行工作。

我想你完成了。 您有两个可爱的递归函数,以及对它们的单行调用。

我只能想到一种骇人听闻的方法,甚至可以在一个功能中做到这一点:

void
mystrcat(char* to, const char* from, bool copy_now)
{
    if (!copy_now && *to)
    {
        mystrcat(to+1, from, false);
        return;
    }

    *to = *from;
    if (*to)
        mystrcat(to+1, from+1, true);
}

如果您使用的是C ++,并且将copy_now设置为默认值为false的可选参数,则几乎不会copy_now 然后,调用方可以假装它没有多余的状态。

但是,如果问题很难解决,无法在StackOverflow上寻求帮助,那么就很难成为一个合理的面试问题。 您可以要求它只是想让这个人开箱即用,但不要因为自动失败而失败。

我建议添加另一个可爱的递归问题:找到二叉树的深度。

给定以下结构:

typedef struct s_node
{
    struct s_node *pLeft;
    struct s_node *pRight;
} NODE;

写一个函数

int tree_depth(NODE *pNode)

返回树的最大深度。 实际上,一次面试曾有人问过我这个问题,我认为这只是面试的适当难度。 您不想雇用的人会遇到麻烦,而您可能想雇用的人可能会喜欢它。

编辑:我测试了该功能,发现它有一个错误。 此版本已修复错误。

您想要递归和简洁吗? 我认为有些东西在没有过多涉及高尔夫杂草的代码的情况下具有两种特质(这并不是说它并不难看-是的):

char* myStrcat( char* to, char const* from)
{
    if (!*from) return to;

    if (!*to) *to++ = *from++, *to-- = '\0';

    return myStrcat( to+1, from) - 1;
}

请注意,此版本具有返回目标指针的复杂性(如标准strcat() )。

这是一个更具可读性的版本,它使用较少的“伪智能”,例如逗号运算符,并在递增以还原to指针后递减(steveha的注释促使我出于某种原因执行此操作)。 再看一下这里的各种答案,几乎等同于走秀的答案

char* myStrcat( char* to, char const* from)
{
    if (!*from) return to;  // terminal condition, no more chars to concatenate

    if (!*to) {
        // since we're at the end of the 'to' string, 
        //    append char from 'from'
        //    and 'consume' it from `from`
        *to = *from++;
        *(to+1) = '\0';
    }

    return myStrcat( to+1, from) - 1;
}

但是,请不要将我的名字与任何该代码关联(我声称是从别人那里偷来的)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM