递归反转输出字符串(不改变字符串本身)——分别用链表和动态数组两种数据结构来实现.C

递归方法虽然写起来简单,但递归公式的寻找可比想到首尾递进互换的方法难太多了,想了很长时间才想出来,初学小菜鸡,在这里回顾一下解题思路。

动态数组的实现:使用malloc函数申请初始大小为N的一片堆空间,使用getchar函数循环读取标准输入缓冲区的字符并存入申请的空间中,当空间被占满时使用realloc函数扩容然后继续读取并写入,遇到换行符时停止读取,在最后一个字符后追加‘\0‘并退出循环。

链表的实现:使用单向链表即可,使用getchar循环读取标准输入缓冲区的字符并使用尾插法建立链表,表尾元素的pNext指针指向NULL。

递归公式的推导:字符串存储下来之后,经过观察,如果要将n个字符反转输出,需要先将后n-1个字符整体反转输出然后输出第1个字符,同理,要将后n-1个字符反转输出,需要先将其后n-2个字符反转输出然后输出第2个字符,以此类推直到反转最后一个字符时将最后一个字符直接打印输出即可结束递归,函数逐层退栈连环打印实现反转输出。

以“ABCDE”为例,定义一个reverse()函数,递归公式可表示为reverse(“ABCDE”) = reverse(“BCDE”) + ‘A’ = reverse(“CDE”) + ‘B’ + ‘A’ = reverse(“DE”) + ‘C’ + ‘B’ + ‘A’ = reverse(‘E’) + ‘D’ + ‘C’ + ‘B’ + ‘A’ = ‘E’ + ‘D’ + ‘C’ + ‘B’ + ‘A’

简单来说,若字符串首地址为p,则反转递归公式为reverse(p): reverse(p+1) and printf(“%c”,*p)

代码实现:

动态数组存储↓

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#pragma warning(disable:4996)
#define N 20

char *record(char *c)
{
	int i,j,len;
	i = j = 0;
	len = N;//局部变量只在最近的一个大括号内有效
	char r;
	for (i; i < len; i++)
	{
		r = getchar();
		if (r != '\n')
		{
			c[i] = r;
			j++;
			if (j == len) {
				c = (char*)realloc(c, len * 2);//使用realloc扩容后,原地址失效,数据被复制到新的地址
				len *= 2;
				continue;
			}
		}
		else 
		{
			c[i] = '\0';
			return c;
		}
	}
}

void reverse(char *head)
{
	if (*(head + 1) == '\0')
	{
		printf("%c", *head);
	}
	else {
		reverse(head + 1);
		printf("%c", *head);
	}
}

int main()
{
	char *c;
	c = (char*)malloc(N);
	c = record(c);//根据键盘输入字符串的长度动态分配数组空间
	printf("-----after reversing-----\n");
	reverse(c);//成功!
	free(c);
	c = NULL;
}

测试结果:《递归反转输出字符串(不改变字符串本身)——分别用链表和动态数组两种数据结构来实现.C》

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

链表存储↓

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#pragma warning(disable:4996)

typedef struct character{
	char c;
	struct character *pNext;
}Character, *pCharacter;

void tail_insert(pCharacter *pHead, pCharacter *pTail,char C)
{
	pCharacter pNew;
	pNew = (pCharacter)calloc(1, sizeof(Character));//插入前先构造新节点
	memset(pNew, 0, sizeof(Character));//将新结点的内存初始化为0(NULL)
	(*pNew).c = C;//将结点的c值初始化为新传入的字符,等价于pNew->c
	if (*pHead == NULL)//当链表为空时
	{
		*pHead = *pTail = pNew;
	}
	else {
		(*pTail)->pNext = pNew;
		*pTail = pNew;
	}
}

void reverse(pCharacter pCur)
{
	if (pCur->pNext == NULL)//当访问到最后一个结点
	{
		printf("%c", pCur->c);
	}
	else {
		pCharacter pPre = pCur;
		reverse(pCur->pNext);
		printf("%c",pPre->c);
	}
}
void print_list(pCharacter p)
{
	while(p != NULL)
	{
		printf("%c", p->c);
		p = p->pNext;
	}
	printf("\n");
}

int main()
{
	char c;//用来接收键盘输入的每个字符
	pCharacter pHead, pTail;//用来辅助增删改查的头尾指针
	pHead = pTail = NULL;
	while (1)
	{
		c = getchar();
		if (c == '\n') break;
		tail_insert(&pHead, &pTail, c);
	}
	printf("----print data in the list----\n");
	print_list(pHead);
	printf("----after reversing----\n");
	reverse(pHead);//反转输出链表数据,不改变链表内存状态和头尾指针
}

测试结果:《递归反转输出字符串(不改变字符串本身)——分别用链表和动态数组两种数据结构来实现.C》

    原文作者:17858790698
    原文地址: https://blog.csdn.net/wildot/article/details/118661765
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞