题目描述
报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下:
1 1
2 11
3 21
4 1211
5 111221
1 被读作 “one 1” (“一个一”) , 即 11。 11 被读作 “two 1s” (“两个一”), 即 21。 21 被读作
“one 2”, “one 1” (“一个二” , “一个一”) , 即 1211。给定一个正整数 n(1 ≤ n ≤ 30),输出报数序列的第 n 项。
注意:整数顺序将表示为一个字符串。
示例 1:
输入: 1 输出: “1”
示例 2:
输入: 4 输出: “1211”
解题思路
这道题激起了我强大的好奇心,好想知道用时最短的那个是不是把答案都算出来然后switch case。想想还有点小激动。
这种类型的题我一般先看看有没有什么通解,有的话列个算式就出来了,然而没找着。
第二种思路就是递归,或者迭代。
这道题我用的递归的方法,步骤如下:
- n = 1时,返回“1”;
- n != 1时,调用此函数本身获取 n-1的结果;
- 获取到n-1的读数后,计算得到n的读数,返回。
代码
char * countAndSay(int n) {
char *pcRet = 0;
int i = 0;
int iStrLen = 0;
int iLastStrLen = 0;
int iRetLen = 0;
int iCount = 1;
int iIndex = 0;
char *pcLastRet = 0;
if (1 == n)
{
pcRet = (char *) malloc(2 * sizeof(char));
pcRet[1] = 0;
*pcRet = '1';
return pcRet;
}
pcLastRet = countAndSay(n - 1);
/* 第一次遍历获取需要内存的大小 */
iLastStrLen = strlen(pcLastRet);
for ( i = 0; i < iLastStrLen; i++)
{
if (pcLastRet[i] != pcLastRet[i+1])
{
iRetLen++;
}
}
pcRet = (char *) malloc((iRetLen * 2 + 1) * sizeof(char));
pcRet[iRetLen * 2] = 0;
/* 填充的字符串 */
for ( i = 0; i < iLastStrLen; i++)
{
if (pcLastRet[i] != pcLastRet[i + 1])
{
pcRet[iIndex++] = iCount + '0';
pcRet[iIndex++] = pcLastRet[i];
iCount = 1;
}
else
{
iCount++;
}
}
free(pcLastRet);
return pcRet;
}
易错点:
说是易错点,其实是我出错的地方。
我果然。。。又又又又一次内存越界了。
其他,感觉就是字符串末尾别忘了加‘0’,边界值要算一下这种吧。
运行结果
看答案
终于到了令人兴奋的看答案时刻了!
然而很遗憾,并不是我想的那样。。
/* 尾递归 */
char *countHelper(char *s, int n)
{
if (n == 1)
return s;
else
{
//求下一个数
int count;
char ch[10000];
char *p = ch;
//一直读数
while (*s!='\0')
{
count = 1;
//如果一直是同一个数
while (*s==*(s+1))
{
count++;
s++;
}
//下一个数更新
*p++ = (char)(count+'0');
*p++ = *s++;
}
return countHelper(ch, n - 1);
}
}
char *countAndSay(int n)
{
return countHelper("1", n);
}
这个n竟然是计算的次数。。感觉这代码写的比起递归更像是迭代,直接写个循环多好。
而且,这是返回了一个动态变量吗??还是我理解错了??
总结
哪天数组没访问越界我一定要吃好吃的庆祝下。