我正在发明需要分析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()
不是很递归 - 它只调用两个其他函数一次。有没有办法重写,以某种方式减少函数的数量,增加递归的使用但不牺牲大量的代码简洁?
答案 0 :(得分:4)
好吧,我并没有真正看到尝试使strcat()“尽可能递归”的重点,即使它只是面试问题。 strcpy()只是一个strlen()+ strcat(),实现应该反映出来。如果候选人给出了你已经勾勒出的解决方案,我会非常高兴 - 他/她表明他/她理解1.递归和2.使用子程序来实现更复杂的功能。
答案 1 :(得分:4)
这是我的例子(优点是它只有一个递归调用):
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);
}
答案 2 :(得分:3)
怎么样:
void mystrcat( char* to, const char* from )
{
if (*to == '\0')
mystrcpy( to, from );
else
mystrcat( to + 1, from );
}
答案 3 :(得分:2)
在单个函数中,您可以写:
// 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条件也是故意丑陋的,因为大多数情况下你会在条件中标记一个=作为拼写错误,但坚持认为它已经启动,运行和测试,以便他们必须自己处理意义。
您可能考虑的另一件事是添加一个返回语句,如:
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的this article,其中包括Scheimel绘制算法)。您可以根据strcat
和mystrcat
要求执行此功能:
// 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] );
}
}
答案 4 :(得分:2)
我想到了另一个答案!
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)。
我不确定在求职面试的压力下我能否想出这个想法。
答案 5 :(得分:2)
我曾经有一个面试问题,我必须编写一个简单的,递归的BSP树遍历,这是一个完整程序中实际的,消隐的功能。
我建议像这样的东西可以更好地衡量某人对递归的掌握,而不是发明一个在C ++中用递归解决的问题。此外,当您运行程序并且他们观察到他们的代码完美运行时,对入门级申请人来说是令人兴奋的。
如果你坚持使用strcat,我认为如果任务是以递归方式连接从链表中获取的任意数量的字符串,那可能会更有趣。即使数据复制和字符串长度操作不是递归的,链接列表的遍历也可能是,并且我认为它会更有趣,因为您实际上可以在向下和在备份的路上进行工作。 / p>
答案 6 :(得分:1)
我认为你已经完成了。你有两个可爱的递归函数,以及对它们的单行调用。
我只能想到一种甚至在一个功能中执行它的hackish方式:
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
的可选参数,那几乎不会太糟糕。然后调用者可以假装它没有那个额外的状态。
但如果问题很难让你在StackOverflow上寻求帮助,那么成为一个合理的面试问题就太难了。你可以问它只是试图让这个人开箱即用,但不要让自己无法得到一个好的答案。
我建议添加另一个可爱的递归问题:找到二叉树的深度。
给出以下结构:
typedef struct s_node
{
struct s_node *pLeft;
struct s_node *pRight;
} NODE;
写一个函数
int tree_depth(NODE *pNode)
返回树的最大深度。实际上,我曾在一次采访中被问过这个问题,而且我认为面试的难度恰到好处。你不想雇用的人会遇到麻烦,你可能想雇用的人可能会喜欢它。
编辑:我测试了这个功能,发现它有一个bug。此版本修复了错误。答案 7 :(得分:1)
你想要递归和简洁吗?这里有一些我认为具有两种品质而不会进入代码高尔夫杂草的东西(这并不是说它不丑 - 它是):
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的注释促使我出于某种原因这样做)。第二次看这里的各种答案几乎相当于catwalk's answer:
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;
}
但是,请不要将我的姓名与任何此类代码相关联(我声称已将其从其他人那里偷走)。