LeetCode题练习与总结:外观数列

153 篇文章 0 订阅
92 篇文章 0 订阅

一、题目描述

给定一个正整数 n ,输出外观数列的第 n 项。

「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。

你可以将其视作是由递归公式定义的数字字符串序列:

  • countAndSay(1) = "1"
  • countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。

前五项如下:

1.     1
2.     11
3.     21
4.     1211
5.     111221
第一项是数字 1 
描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 "11"
描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 "21"
描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 "1211"
描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 "111221"

描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。

例如,数字字符串 "3322251" 的描述如下图:

示例 1:

输入:n = 1
输出:"1"
解释:这是一个基本样例。

示例 2:

输入:n = 4
输出:"1211"
解释:
countAndSay(1) = "1"
countAndSay(2) = 读 "1" = 一 个 1 = "11"
countAndSay(3) = 读 "11" = 二 个 1 = "21"
countAndSay(4) = 读 "21" = 一 个 2 + 一 个 1 = "12" + "11" = "1211"

提示:

  • 1 <= n <= 30

二、解题思路

  1. 外观数列的生成规则是基于对前一项的描述,其中描述是基于连续相同数字的个数和该数字本身。
  2. 我们需要编写一个函数 countAndSay 来输出外观数列的第 n 项。
  3. 由于这是一个递归问题,我们可以从基本情况开始,即 countAndSay(1) = "1"
  4. 对于 n > 1 的情况,我们需要根据前一项来生成当前项。这涉及到遍历字符串,找到连续相同字符的序列,并将其转换为描述形式(例如,两个连续的 '1' 变成 "21")。
  5. 我们可以通过一个循环来实现这个递归过程,直到达到所需的项数。

三、具体代码

public class Solution {
    public String countAndSay(int n) {
        if (n == 1) return "1";
        String prev = "1";
        for (int i = 2; i <= n; i++) {
            prev = nextTerm(prev);
        }
        return prev;
    }

    private String nextTerm(String term) {
        StringBuilder sb = new StringBuilder();
        int count = 1;
        for (int i = 1; i < term.length(); i++) {
            if (term.charAt(i) == term.charAt(i - 1)) {
                count++;
            } else {
                sb.append(count).append(term.charAt(i - 1));
                count = 1; // Reset count for the new character
            }
        }
        // Append the last character's count and itself
        sb.append(count).append(term.charAt(term.length() - 1));
        return sb.toString();
    }
}

四、时间复杂度和空间复杂度

1. 时间复杂度
  • 该函数包含一个循环,该循环的次数是 n,即我们需要生成的项数。
  • 在每次循环中,我们调用 nextTerm 函数,这个函数的时间复杂度是 O(k),其中 k 是当前项的长度。
  • 由于每一项的长度大约是前一项长度的两倍(这是因为每两个相同的数字会被合并为一个数字加一个计数),所以第 n 项的长度大约是 2^(n-1)
  • 因此,对于 nextTerm 函数,总的时间复杂度大约是 O(2^(n-1) * (n-1)),这是通过将每一项的长度累加得到的。
  • 所以,countAndSay 函数的总时间复杂度大约是 O(n * 2^(n-1))
2. 空间复杂度
  • nextTerm 函数使用了 StringBuilder 来构建结果字符串,其空间大小取决于输入字符串的长度,即 O(k)
  • 由于 countAndSay 函数中没有使用额外的数据结构来存储除了输入和输出之外的任何信息,所以空间复杂度主要取决于 StringBuilder 的使用。
  • 因此,总的空间复杂度也是 O(k),其中 k 是第 n 项的长度。

五、总结知识点

  1. 递归思想:虽然代码实现中使用了迭代,但问题本身可以通过递归来解决。递归是一种编程范式,允许函数调用自身来解决问题。

  2. 字符串操作:代码中使用了 StringBuilder 来构建新的字符串,这是因为 StringBuilder 在字符串拼接操作中比 String 类更高效,尤其是在频繁修改字符串内容时。

  3. 循环结构:代码使用了一个 for 循环来迭代生成外观数列的每一项。循环结构是编程中实现重复任务的基本方法。

  4. 字符比较:在 nextTerm 方法中,通过比较当前字符和前一个字符来判断是否为连续相同的字符。

  5. 条件判断:使用 if 语句来决定何时追加计数和字符到 StringBuilder

  6. 字符串拼接:在 StringBuilder 中使用 append 方法来拼接数字和字符,最终生成新的字符串。

  7. 私有辅助方法nextTerm 是一个私有方法,用于生成外观数列的下一项。这是封装的一个例子,将一个复杂的功能分解成更小、更易于管理的部分。

  8. 返回值:函数通过返回字符串来输出结果,这是函数式编程中常见的一种模式。

  9. 边界条件处理:在函数开始时,检查 n 是否为 1,这是处理基本情况的一种常见做法,确保递归或迭代有一个明确的起始点。

  10. 变量命名:代码中的变量命名清晰,如 prev 表示前一项,count 表示当前字符的连续计数,这有助于代码的可读性和维护性。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

  • 31
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一直学习永不止步

谢谢您的鼓励,我会再接再厉的!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值