用C语言递归
我有下面的代码递归地反转一个字符串,当我在递归完成后打印字符时,它可以工作,但是我不知道如何将反转字符组装成一个字符串并将其反转返回给调用方。有人有主意吗?我不想再增加一个参数来积累字符,就这样,这不是家庭作业,我正在复习一些小东西,因为我将在一年后毕业,需要在面试中表现出色用C语言递归,c,recursion,C,Recursion,我有下面的代码递归地反转一个字符串,当我在递归完成后打印字符时,它可以工作,但是我不知道如何将反转字符组装成一个字符串并将其反转返回给调用方。有人有主意吗?我不想再增加一个参数来积累字符,就这样,这不是家庭作业,我正在复习一些小东西,因为我将在一年后毕业,需要在面试中表现出色 char* reversestring5(char* s) { int i = 0; //Not at null terminator if(*s!=0) { //adv
char* reversestring5(char* s)
{
int i = 0;
//Not at null terminator
if(*s!=0)
{
//advance the pointer
reversestring5(s+1);
printf("%c\n",*s);
}
}
对于递归函数,通常最简单的方法是首先找出如何解决一个简单的情况(例如,用一对字符反转一个字符串),然后看看如何将问题分解为简单的操作,最终以简单的情况结束。例如,可以这样做: 这是实际的递归函数:
char *revrecurse(char *front, char *back)
{
if (front < back) {
char temp = *front;
*front = *back;
*back = temp;
revrecurse(front+1, back-1);
}
return front;
}
char *reverse(char *str)
{
return revrecurse(str, &str[strlen(str)-1]);
}
请注意,这假定指针是有效的,并且它指向以NUL
结尾的字符串
如果要实际反转字符,可以提供一对指针并递归交换字母(这就是此例程所做的),或者将字符一次复制到另一个空间中。这基本上就是您的原始代码所做的;一次将每个字符复制到stdout
,stdout是一个全局结构,未显式传递,但正在由例程使用。类似于该方法,但使用指针可能如下所示:
#define MAXSTRINGLEN 200
char revbuf[MAXSTRINGLEN];
char *refptr = revbuf;
char *revstring(char *s)
{
if (*s != 0)
{
revstring(s+1);
*refptr++ = *s; /* copy non-NUL characters */
} else {
*refptr++ = '\0'; /* copy NUL character */
}
return revbuf;
}
在对原始代码的这个小修改中,您现在可以看到这种方法依赖于全局变量
revbuf
和refptr
,它们隐藏在原始代码的stdout
中。显然,这甚至还没有达到优化的程度——它只是为了解释的目的。如果你真的不能使用帮助函数,你真的不能修改函数的接口,你真的必须使用递归,你可以这样做,尽管这很可怕:
char *str_reverse(char *str)
{
size_t len = strlen(str);
if (len > 1)
{
char c0 = str[0];
char c1 = str[len-1];
str[len-1] = '\0';
(void)str_reverse(str+1);
str[0] = c1;
str[len-1] = c0;
}
return str;
}
这将捕获字符串中的第一个和最后一个字符(您可以在不捕获第一个字符的情况下生存),然后缩短字符串,对缩短的字符串调用函数,然后恢复交换的第一个和最后一个字符。返回值真的没有帮助;我保留它只是为了保持界面不变。当递归调用忽略返回值时,这一点最为明显
请注意,这对性能来说是可怕的,因为它计算了strlen()
(N/2)次,而不是一次。给定一个要反转的千兆字节字符串,这很重要
如果不使用
strlen()
或其等效工具,我想不出编写代码的好方法。要在原位反转字符串,您必须知道端点的位置。由于您规定的接口不包含端点位置的信息,因此您必须在函数中找到端点。例如,我不认为strhr(str,'\0')
与strlen(str)
有显著不同
如果将界面更改为:
void mem_reverse_in_situ(char *start, char *end)
{
if (start < end)
{
char c0 = *start;
*start = *end;
*end = c0;
mem_reverse_in_situ(start+1, end-1);
}
}
void mem\u reverse\u原位(char*start,char*end)
{
如果(开始<结束)
{
字符c0=*开始;
*开始=*结束;
*结束=c0;
mem_反向原位(开始+1,结束-1);
}
}
然后,反转代码避免了字符串长度(或内存长度)的所有问题—需要调用代码来处理它。该函数只需交换两端并在中间段上调用自己。不过,您不会将其作为递归函数编写;您将使用迭代解决方案:
void mem_reverse_in_situ(char *start, char *end)
{
while (start < end)
{
char c0 = *start;
*start++ = *end;
*end-- = c0;
}
}
void mem\u reverse\u原位(char*start,char*end)
{
while(开始<结束)
{
字符c0=*开始;
*开始+=*结束;
*结束--=c0;
}
}
这是一个很好的问题,从其他答案来看,答案涉及的技巧显然很少有人熟悉。这就行了。。。它递归地将字符串转换为一个链表(保存在堆栈上,因此非常有效),该链表表示字符串的反转。然后,它将链表转换回字符串(它以迭代方式执行此操作,但问题语句没有说它不能)。评论中有人抱怨说这是“过度杀伤力”,但任何递归解决方案都是过度杀伤力。。。递归并不是反向处理数组的好方法。但请注意,这种方法可以应用于一整套问题,即动态生成值,而不是让它们在数组中已经可用,然后对它们进行反向处理。因为OP对发展或复习技能感兴趣,这个答案提供了额外的价值。。。因为这种在堆栈上创建链表,然后在终止条件下使用链表的技术(必须在链表的内存超出范围之前)显然并不为人所知。一个例子是回溯算法,比如八皇后问题
针对由于列表到字符串缓冲区的迭代复制而导致这不是“纯递归”的抱怨,我对其进行了更新,以实现两种方式:
#include <stdio.h>
#include <stdlib.h>
typedef struct Cnode Cnode;
struct Cnode
{
char c;
const Cnode* next;
};
static void list_to_string(char* s, const Cnode* np)
{
#ifdef ALL_RECURSIVE
if (np)
{
*s = np->c;
list_to_string(s+1, np->next);
}
else
*s = '\0';
#else
for (; np; np = np->next)
*s++ = np->c;
*s = '\0';
#endif
}
static char* reverse_string_recursive(const char* s, size_t len, const Cnode* np)
{
if (*s)
{
Cnode cn = { *s, np };
return reverse_string_recursive(s+1, len+1, &cn);
}
char* rs = malloc(len+1);
if (rs)
list_to_string(rs, np);
return rs;
}
char* reverse_string(const char* s)
{
return reverse_string_recursive(s, 0, NULL);
}
int main (int argc, char** argv)
{
if (argc > 1)
{
const char* rs = reverse_string(argv[1]);
printf("%s\n", rs? rs : "[malloc failed in reverse_string]");
}
return 0;
}
#包括
#包括
类型定义结构Cnode Cnode;
结构Cnode
{
字符c;
const Cnode*下一步;
};
静态无效列表到字符串(char*s,const Cnode*np)
{
#ifdef ALL_递归
if(np)
{
*s=np->c;
列表到字符串(s+1,np->next);
}
其他的
*s='\0';
#否则
对于(;np;np=np->next)
*s++=np->c;
*s='\0';
#恩迪夫
}
静态字符*反向字符串\递归(常量字符*s,大小长度,常量Cnode*np)
{
如果(*s)
{
Cnode cn={*s,np};
返回反向字符串递归(s+1、len+1和cn);
}
char*rs=malloc(len+1);
如果(卢比)
列表到字符串(rs,np);
返回rs;
}
字符*反向_字符串(常量字符*s)
{
返回反向字符串递归(s,0,NULL);
}
int main(int argc,字符**argv)
{
如果(argc>1)
{
const char*rs=reverse_字符串(argv[1]);
printf(“%s\n”,rs?rs
#include <stdio.h>
#include <stdlib.h>
typedef struct Cnode Cnode;
struct Cnode
{
char c;
const Cnode* next;
};
static void list_to_string(char* s, const Cnode* np)
{
#ifdef ALL_RECURSIVE
if (np)
{
*s = np->c;
list_to_string(s+1, np->next);
}
else
*s = '\0';
#else
for (; np; np = np->next)
*s++ = np->c;
*s = '\0';
#endif
}
static char* reverse_string_recursive(const char* s, size_t len, const Cnode* np)
{
if (*s)
{
Cnode cn = { *s, np };
return reverse_string_recursive(s+1, len+1, &cn);
}
char* rs = malloc(len+1);
if (rs)
list_to_string(rs, np);
return rs;
}
char* reverse_string(const char* s)
{
return reverse_string_recursive(s, 0, NULL);
}
int main (int argc, char** argv)
{
if (argc > 1)
{
const char* rs = reverse_string(argv[1]);
printf("%s\n", rs? rs : "[malloc failed in reverse_string]");
}
return 0;
}
void reversestring_impl(char* s, char **dst)
{
if (*s != '\0')
{
reversestring_impl(s + 1, dst);
*(*dst)++ = *s;
}
}
void reversestring5(char* s)
{
char *dst = s;
reversestring_impl(s, &dst);
}
void reversestring_impl(char* s, char **dst)
{
if (*s != '\0')
{
char c = *s;
reversestring_impl(s + 1, dst);
*(*dst)++ = c;
}
}
void reversestring5(char* s)
{
char *dst = s;
reversestring_impl(s, &dst);
}
int main()
{
char str[] = "123456789";
reversestring5(str);
printf("%s\n", str);
}
static void swap(char* lo, char* hi) {
char tmp = *hi;
*hi = *lo;
*lo = tmp;
}
static char* step(char* tortoise, char* hare) {
if (hare[0]) return tortoise;
if (hare[1]) return tortoise + 1;
hare = step(tortoise + 1, hare + 2);
swap(tortoise, hare);
return hare + 1;
}
void reverse_in_place(char* str) { step(str, str); }