leetcode外观数列

1.题目

「外观数列」是一个数位字符串序列,由递归公式定义:

countAndSay(1) = "1"
countAndSay(n) 是 countAndSay(n-1) 的行程长度编码。
 

行程长度编码(RLE)是一种字符串压缩方法,其工作原理是通过将连续相同字符(重复两次或更多次)替换为字符重复次数(运行长度)和字符的串联。例如,要压缩字符串 "3322251" ,我们将 "33" 用 "23" 替换,将 "222" 用 "32" 替换,将 "5" 用 "15" 替换并将 "1" 用 "11" 替换。因此压缩后字符串变为 "23321511"。

给定一个整数 n ,返回 外观数列 的第 n 个元素。

2.示例

示例 1:

输入:n = 4

输出:"1211"

解释:

countAndSay(1) = "1"

countAndSay(2) = "1" 的行程长度编码 = "11"

countAndSay(3) = "11" 的行程长度编码 = "21"

countAndSay(4) = "21" 的行程长度编码 = "1211"

示例 2:

输入:n = 1

输出:"1"

解释:

这是基本情况。

提示:

1 <= n <= 30

3.分析

(1)第一种:非递归

使用3层循环:

第一层循环是用来从i==2到i==n的循环,每一次循环结束就可以得到n==i时的外观数列;

第二层循环是用来遍历上一轮得到的外观数列并生成新的外观数列;

第三层循环是用来计算上一轮得到的外观数列中每一位元素,连续存在的个数;

(2)第二种:递归

从i==n递归到i==1,然后再从i==1返回到i==n。每一次返回的是i的外观数列。

每一层递归最重要的是用一个双层循环构造i的外观数列。

第一层循环遍历返回的外观数列。

第二层循环计算外观数列重每个元素连续存在的个数,并构造新的外观数列。

最后得到新的外观数列后,就返回给上一层。

4.代码

(1)非递归

char* countAndSay(int n) {
	//起始空间大小为2
	char *result = (char*)malloc(2);
	*(result + 0) = '1';//当n==1时,它的外观数列时“1”
	*(result + 1) = '\0';//开辟的对空间一定要多一个空间用来存储'\0',否则后面的遍历会出错

	//如果n==1,直接返回外观数列"1"
	if (n == 1)
	{
		return result;
	}

	//如果n>=2,就通过如下循环进行计算。
	for (int i = 2; i <= n; i++)
	{
		//保存原有的堆空间
		char *temp_result=temp_result = result;

		//这里开辟一个新的空间用于存储新的外观数列,这里起始空间大小只有1个字节,存储'\0'
		result = (char*)malloc(1);
		*result = '\0';

		size_t room = 2;//这个变量用来记录开辟新空间的空间大小,初始值为2

		//这个双层for循环就是用来计算每个元素的个数,并把得到的个数和元素保存到新的堆空间中
		//j用来遍历原来的外观数列;m用来记录每个元素应该保存到新的堆空间的位置;room表示每一次循环时,开辟新空间的大小
		for (int j = 0, m = 0; *(temp_result + j) != '\0'; room+=2, m += 2)
		{
			//得到每个元素的个数
			int count = 1;
			for (int t = j + 1; *(temp_result + t) != '\0'; t++)
			{
				if ((*(temp_result + t)) == (*(temp_result + j)))
				{
					count++;
				}
				else break;
			}

			//得到*(temp_result + j)元素的个数后,就要开辟新的空间,这个新的空间不仅要保存原来堆空间中已经存在的元素,还要存储*(temp_result + j)和它的个数,因此
			//每次m+=2;
			result = (char*)realloc(result, room);

			//存储新的元素及其个数
			*(result + m) = '0' + count;
			*(result + m + 1) = *(temp_result + j);

			//每次向后移动的步数肯定是要把连续相等的元素全部跳过,因此是j+=count;
			j += count;

		}
		room -= 1;//这里romm-=1,是因为上一个for循环结束时多加了一个2,因此要减一

		//再申请一个新的空间,这个空间比原空间大一,目的是要在元空间中的外观数列末尾加入一个'\0',否则下一轮循环会出错
		result = (char*)realloc(result, room);
		*(result + room-1) = '\0';

		//释放上一个存储外观数列的堆空间
		free(temp_result);
	}

	//返回存储外观数列的堆空间。
	return result;

}

(2)递归

#define MAX_STR_LEN 10000  //每次申请的堆空间大小,用来保存每一次迭代的外观数列

//每一次递归结束就可以得到一个递归数列
char * countAndSay(int n)
{
    //申请堆空间,并初始化
    char *result = (char *)malloc(sizeof(char) * MAX_STR_LEN);
    memset(result, 0, sizeof(char) * MAX_STR_LEN);

    //如果递归到n==1时,就可以返回了
    if (n == 1) {
        result[0] = '1';
        return result;
    }


    //得到n-1的外观数列
    char *tmp = countAndSay(n - 1);
    int len = strlen(tmp);//计算外观数列的长度

    //构造n的改观数列
    int continueCnt = 0; // 连续数字的个数
    int cnt = 0;
    for (int i = 0; i < len; i++) {
        continueCnt++;
        int next = i + 1;
        //如果已经到外观数列末尾或者本轮循环的元素与下一轮元素不相同,就表示tno[i]这个元素的个数统计结束
        if (next >= len || tmp[next] != tmp[i]) {
            //构造新的外观上数列
            result[cnt++] = '0' + continueCnt;//赋值个数
            result[cnt++] = tmp[i];//赋值本轮元素

            //个数清零
            continueCnt = 0;
        }
    }

    //释放堆空间
    free(tmp);
    
    //结尾赋值'\0'表示结束符,否则返回到上一层时,用strlen会出错。开辟的堆空间时不会自动赋值'\0'
    result[cnt] = '\0';

    //返回结果
    return result;
}

5.总结

对c语言的堆空间开辟函数要熟悉。其他的没有什么难度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值