返回
巧解LeetCode 38:外观数列的巧妙递归实现
前端
2023-11-07 18:56:20
引言
LeetCode 38:外观数列是一道经典的编程题,它考验了递归和字符串操作的技巧。所谓外观数列,是一个从数字 1 开始的整数序列,其中每一项都是对前一项的。例如:
1 -> "1"
11 -> "11"
21 -> "12"
1211 -> "1112"
111221 -> "312211"
由此可见,外观数列的生成规则是:对于序列中的第 n 项,先第 n-1 项中各个数字出现的次数,再将这些描述串联起来。
递归实现
基于外观数列的生成规则,我们可以使用递归来巧妙地实现它。以下是 Python 中的递归实现:
def count_and_say(n):
if n == 1:
return "1"
else:
prev = count_and_say(n - 1)
result = []
count = 1
for i in range(1, len(prev) + 1):
if i < len(prev) and prev[i] == prev[i - 1]:
count += 1
else:
result.append(str(count))
result.append(prev[i - 1])
count = 1
return "".join(result)
在这个递归函数中,我们首先判断 n 是否为 1,如果是,则返回 "1"。否则,我们递归调用 count_and_say(n - 1) 获取前一项。然后,我们遍历前一项,统计每个数字出现的次数,并将这些次数和对应的数字添加到 result 列表中。最后,我们将 result 列表中的元素连接起来,得到外观数列的第 n 项。
时间复杂度
递归实现的时间复杂度为 O(n^2),因为对于外观数列的第 n 项,我们需要遍历前一项,而前一项的时间复杂度也是 O(n)。
动态规划
除了递归实现,还可以使用动态规划来解决这道题。动态规划的核心思想是将问题分解成子问题,并存储子问题的解,以避免重复计算。
def count_and_say_dp(n):
dp = ["1"]
for i in range(1, n):
prev = dp[i - 1]
result = []
count = 1
for j in range(1, len(prev) + 1):
if j < len(prev) and prev[j] == prev[j - 1]:
count += 1
else:
result.append(str(count))
result.append(prev[j - 1])
count = 1
dp.append("".join(result))
return dp[n - 1]
在动态规划实现中,我们使用 dp 列表存储外观数列的前 n 项。对于第 n 项,我们直接从 dp 列表中获取,避免了递归过程中的重复计算。因此,动态规划实现的时间复杂度为 O(n^2),与递归实现相同。
总结
LeetCode 38:外观数列是一道经典的编程题,它考验了递归和字符串操作的技巧。通过使用巧妙的递归或动态规划实现,我们可以高效地生成外观数列的第 n 项。掌握外观数列的生成规则,不仅可以帮助我们解决这道编程题,更能加深我们对字符串操作和递归算法的理解。