摘要
今天我们来聊聊 LeetCode 247 这道题,叫 Strobogrammatic Number II(对称数 II)。简单说,它就是让我们找出所有长度为 n
的对称数——也就是 旋转 180° 后仍然保持原样的数字。
这道题的思路很有意思,直接暴力枚举是不行的,我们要用 递归+回溯 来一步步构造答案。本文会 分析题目难点、提供 Swift 代码、解析解法,还会结合实际应用场景,让你彻底搞懂这个问题。
描述
什么是对称数?
对称数就是 旋转 180° 后仍然是原来的数。换句话说,数码在翻转后还能保持原样,比如:
原数字 | 旋转 180° 后 |
---|---|
0 | 0 |
1 | 1 |
6 | 9 |
8 | 8 |
9 | 6 |
比如 69,翻转后还是 69,但 12 翻转后变成 21,就不是对称数。
题目示例
输入: n = 2
输出: ["11", "69", "88", "96"]
痛点分析
这道题看似简单,但其实有几个难点:
-
不能暴力枚举
- 比如
n = 2
的情况下,我们可以直接枚举 10-99 的所有两位数,然后挨个判断是不是对称数。但如果n = 14
,那就是 10¹³ 到 10¹⁴ 之间的所有数,范围太大,完全不现实。
- 比如
-
不是所有数字都能用
- 数字 2, 3, 4, 5, 7 旋转 180° 后没法变成有效的数字,所以这些数字根本不能用,限制了我们的选择。
-
递归构造,而不是遍历
- 我们不能直接从
00
开始找,而是要 从小到大递归地生成,比如:- 先找到
n=0
和n=1
的基本情况(空字符串""
、"0"
、"1"
、"8"
)。 - 然后递归地往两边加对称数,比如
"1" + num + "1"
,形成n=3
的情况。
- 先找到
- 我们不能直接从
-
防止前导零
n > 1
的情况下,“0” 不能出现在最前面,否则会变成无效数字,比如 “069” 其实是 “69” 而已,不算一个有效的三位数。
Swift 题解
我们使用 递归 + 回溯 解决这个问题,思路是:
- 先递归生成更短的对称数
- 再在它们的两边拼接对称数字对(
"0" + num + "0"
、"1" + num + "1"
等)
class Solution {
func findStrobogrammatic(_ n: Int) -> [String] {
return helper(n, n)
}
private func helper(_ m: Int, _ n: Int) -> [String] {
if m == 0 { return [""] }
if m == 1 { return ["0", "1", "8"] }
let subList = helper(m - 2, n)
var result: [String] = []
for num in subList {
if m != n { result.append("0" + num + "0") }
result.append("1" + num + "1")
result.append("6" + num + "9")
result.append("8" + num + "8")
result.append("9" + num + "6")
}
return result
}
}
题解代码分析
-
递归终止条件
m == 0
时,返回[""]
(空字符串,用于拼接)。m == 1
时,返回["0", "1", "8"]
,因为单个字符的对称数只有这三个。
-
递归构造
- 先递归生成 更短的对称数,比如
n = 4
时,我们先找到n = 2
的情况["11", "69", "88", "96"]
。 - 然后用 五种可能的对称组合 进行扩展:
"0" + num + "0"
(但不能用于最外层)"1" + num + "1"
"6" + num + "9"
"8" + num + "8"
"9" + num + "6"
- 先递归生成 更短的对称数,比如
示例测试及结果
let solution = Solution()
print(solution.findStrobogrammatic(2))
输出
["11", "69", "88", "96"]
如果 n = 3
,会得到:
["101", "111", "181", "609", "619", "689", "808", "818", "888", "906", "916", "986"]
时间复杂度
- 最坏情况下,每一层递归会产生 5 倍的子问题,总共有
O(n/2)
层递归:- 时间复杂度:
O(5^(n/2))
- 空间复杂度:
O(5^(n/2))
(存储所有结果)
- 时间复杂度:
虽然时间复杂度是指数级的,但 n
最大才 14,计算量还能接受。
实际应用场景
-
对称验证码
- 有些网站会生成特殊验证码,在旋转屏幕时仍然能看清。比如某些验证码可能使用
818
、609
这类对称数。
- 有些网站会生成特殊验证码,在旋转屏幕时仍然能看清。比如某些验证码可能使用
-
车牌号 & 竞赛车号
- 在一些比赛中,车号设计要 前后对称,方便观众从不同角度识别,比如
88
、96
这种数。
- 在一些比赛中,车号设计要 前后对称,方便观众从不同角度识别,比如
-
对称性设计
- 在某些艺术设计、标志、Logo 里,我们可能会用到 旋转对称 的数字,比如某些品牌 Logo 可能采用 6 和 9 互换的效果。
总结
- 这道题 不能暴力遍历,要用 递归+回溯 构造解法。
- 关键点是 递归构造对称数,并且要 防止前导零。
- 时间复杂度较高,但
n <= 14
仍然可接受。 - 应用场景广泛,从验证码到车牌号、艺术设计,都可能用到类似的对称性概念。