Missing undo step on backtrack
Wrong move: Mutable state leaks between branches.
Usually fails on: Later branches inherit selections from earlier branches.
Fix: Always revert state changes immediately after recursive call.
Move from brute-force thinking to an efficient approach using backtracking strategy.
Given two integers n and k, return an array of all the integers of length n where the difference between every two consecutive digits is k. You may return the answer in any order.
Note that the integers should not have leading zeros. Integers as 02 and 043 are not allowed.
Example 1:
Input: n = 3, k = 7 Output: [181,292,707,818,929] Explanation: Note that 070 is not a valid number, because it has leading zeroes.
Example 2:
Input: n = 2, k = 1 Output: [10,12,21,23,32,34,43,45,54,56,65,67,76,78,87,89,98]
Constraints:
2 <= n <= 90 <= k <= 9Problem summary: Given two integers n and k, return an array of all the integers of length n where the difference between every two consecutive digits is k. You may return the answer in any order. Note that the integers should not have leading zeros. Integers as 02 and 043 are not allowed.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Backtracking
3 7
2 1
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #967: Numbers With Same Consecutive Differences
class Solution {
private List<Integer> ans = new ArrayList<>();
private int boundary;
private int k;
public int[] numsSameConsecDiff(int n, int k) {
this.k = k;
boundary = (int) Math.pow(10, n - 1);
for (int i = 1; i < 10; ++i) {
dfs(i);
}
return ans.stream().mapToInt(i -> i).toArray();
}
private void dfs(int x) {
if (x >= boundary) {
ans.add(x);
return;
}
int last = x % 10;
if (last + k < 10) {
dfs(x * 10 + last + k);
}
if (k != 0 && last - k >= 0) {
dfs(x * 10 + last - k);
}
}
}
// Accepted solution for LeetCode #967: Numbers With Same Consecutive Differences
func numsSameConsecDiff(n int, k int) (ans []int) {
bounary := int(math.Pow10(n - 1))
var dfs func(int)
dfs = func(x int) {
if x >= bounary {
ans = append(ans, x)
return
}
last := x % 10
if last+k < 10 {
dfs(x*10 + last + k)
}
if k > 0 && last-k >= 0 {
dfs(x*10 + last - k)
}
}
for i := 1; i < 10; i++ {
dfs(i)
}
return
}
# Accepted solution for LeetCode #967: Numbers With Same Consecutive Differences
class 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
// Accepted solution for LeetCode #967: Numbers With Same Consecutive Differences
struct Solution;
impl Solution {
fn nums_same_consec_diff(n: i32, k: i32) -> Vec<i32> {
let n = n as usize;
let mut cur: Vec<i32> = vec![];
let mut res = vec![];
for i in 0..10 {
if i == 0 && n != 1 {
continue;
}
cur.push(i);
Self::dfs(1, &mut cur, &mut res, k, n);
cur.pop();
}
res
}
fn dfs(start: usize, cur: &mut Vec<i32>, all: &mut Vec<i32>, k: i32, n: usize) {
if start == n {
let mut x = 0;
for i in 0..n {
x *= 10;
x += cur[i];
}
all.push(x);
} else {
for i in 0..10 {
if (cur[start - 1] - i).abs() != k {
continue;
}
cur.push(i);
Self::dfs(start + 1, cur, all, k, n);
cur.pop();
}
}
}
}
#[test]
fn test() {
let n = 3;
let k = 7;
let res = vec![181, 292, 707, 818, 929];
assert_eq!(Solution::nums_same_consec_diff(n, k), res);
let n = 2;
let k = 1;
let res = vec![
10, 12, 21, 23, 32, 34, 43, 45, 54, 56, 65, 67, 76, 78, 87, 89, 98,
];
assert_eq!(Solution::nums_same_consec_diff(n, k), res);
}
// Accepted solution for LeetCode #967: Numbers With Same Consecutive Differences
function numsSameConsecDiff(n: number, k: number): number[] {
const ans: number[] = [];
const boundary = 10 ** (n - 1);
const dfs = (x: number) => {
if (x >= boundary) {
ans.push(x);
return;
}
const last = x % 10;
if (last + k < 10) {
dfs(x * 10 + last + k);
}
if (k > 0 && last - k >= 0) {
dfs(x * 10 + last - k);
}
};
for (let i = 1; i < 10; i++) {
dfs(i);
}
return ans;
}
Use this to step through a reusable interview workflow for this problem.
Generate every possible combination without any filtering. At each of n positions we choose from up to n options, giving nⁿ total candidates. Each candidate takes O(n) to validate. No pruning means we waste time on clearly invalid partial solutions.
Backtracking explores a decision tree, but prunes branches that violate constraints early. Worst case is still factorial or exponential, but pruning dramatically reduces the constant factor in practice. Space is the recursion depth (usually O(n) for n-level decisions).
Review these before coding to avoid predictable interview regressions.
Wrong move: Mutable state leaks between branches.
Usually fails on: Later branches inherit selections from earlier branches.
Fix: Always revert state changes immediately after recursive call.