题目描述
思路解析动画文字版
记住这套「定起头、每位只 ±k、越界就剪、满 n 位收一个」,下面每一帧都在套它。
上面一排是 0 到 9 十个数字,每一位都从这里挑。下面 path 是正在拼的数。规矩:除第一位外,每一位都等于上一位加 k 或减 k,且必须落在 0 到 9 里。这道题 n = 3、k = 7。
第一位从 1 开始。开头只能选 1 到 9,不能用 0,否则就成了前导零。path 现在是 [1],接着往下看下一位。
末位是 1:加 7 得 8,没超过 9,放下去;减 7 会得 -6,是负数,那支不通。path 变成 [1, 8]。
末位是 8:加 7 得 15,超过 9 了先剪掉;减 7 得 1,合法,放下去。path 变成 [1, 8, 1]。
又凑齐三位,[1, 8, 1] 就是 181,相邻差都是 7,收进结果区。
回到最前面,第一位换成 2,重新往下拼。path 现在是 [2]。
末位是 2:加 7 得 9,没超过 9,放下去;减 7 会得 -5,是负数,那支不通。path 变成 [2, 9]。
末位是 9:加 7 得 16,超过 9 了先剪掉;减 7 得 2,合法,放下去。path 变成 [2, 9, 2]。
又凑齐三位,[2, 9, 2] 就是 292,相邻差都是 7,收进结果区。
第一位试 3:加 7 得 10 越界,减 7 得 -4 是负数,两边都迈不出去,这个起头作废,换下一个。
第一位试 4:加 7 得 11 越界,减 7 得 -3 是负数,两边都迈不出去,这个起头作废,换下一个。
第一位试 5:加 7 得 12 越界,减 7 得 -2 是负数,两边都迈不出去,这个起头作废,换下一个。
第一位试 6:加 7 得 13 越界,减 7 得 -1 是负数,两边都迈不出去,这个起头作废,换下一个。
回到最前面,第一位换成 7,重新往下拼。path 现在是 [7]。
末位是 7:加 7 得 14,超过 9 了先剪掉;减 7 得 0,合法,放下去。path 变成 [7, 0]。
末位是 0:加 7 得 7,没超过 9,放下去;减 7 会得 -7,是负数,那支不通。path 变成 [7, 0, 7]。
三位凑齐了,[7, 0, 7] 连起来就是 707,相邻两位差都正好 7,合格,收进右边结果区。
回到最前面,第一位换成 8,重新往下拼。path 现在是 [8]。
末位是 8:加 7 得 15,超过 9 了先剪掉;减 7 得 1,合法,放下去。path 变成 [8, 1]。
末位是 1:加 7 得 8,没超过 9,放下去;减 7 会得 -6,是负数,那支不通。path 变成 [8, 1, 8]。
三位凑齐了,[8, 1, 8] 连起来就是 818,相邻两位差都正好 7,合格,收进右边结果区。
回到最前面,第一位换成 9,重新往下拼。path 现在是 [9]。
末位是 9:加 7 得 16,超过 9 了先剪掉;减 7 得 2,合法,放下去。path 变成 [9, 2]。
末位是 2:加 7 得 9,没超过 9,放下去;减 7 会得 -5,是负数,那支不通。path 变成 [9, 2, 9]。
三位凑齐了,[9, 2, 9] 连起来就是 929,相邻两位差都正好 7,合格,收进右边结果区。
九个起头全试完,合法的数依次是 181、292、707、818、929,一共 5 个,这就是答案。
边界先想清:k 为 0 全是叠字、k 为 9 几乎无路、n 一大候选呈指数膨胀。
面试重点:识别出「逐位 ±k 扩展 + 越界剪枝」,深搜广搜两套都能讲。
参考代码
from __future__ import annotationsfrom typing import *from collections import *from functools import *from itertools import *from math import *from heapq import *from bisect import *class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = rightclass ListNode: def __init__(self, val=0, next=None): self.val = val self.next = nextclass Solution: def numsSameConsecDiff(self, n: int, k: int) -> List[int]: def dfs(x: int): if x >= boundary: ans.append(x) return last = x % 10 if last + k <= 9: dfs(x * 10 + last + k) if last - k >= 0 and k != 0: dfs(x * 10 + last - k) ans = [] boundary = 10 ** (n - 1) for i in range(1, 10): dfs(i) return ans复杂度
- 时间:O(2ⁿ),第一位 9 种,之后每位最多两支,约 9·2ⁿ⁻¹ 个候选,每个拼接 O(n)
- 空间:O(n),递归栈深度等于位数 n,不计输出数组
易错点
面试追问把动画讲成自己的话
追问这题为什么用深搜,不直接枚举所有 n 位数再筛?
追问能不能改成广度优先?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
活字印刷
LeetCode 1079 · 中等 · 沿着 回溯套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题