🧠
Backtracking with Early Stop
💡 This approach improves brute force by stopping recursion once the k-th permutation is found, saving some time but still not efficient enough for large n.
Intuition
Generate permutations in lex order, but stop recursion as soon as the k-th permutation is reached.
Algorithm
- Initialize a global counter to track how many permutations have been generated.
- Use backtracking to generate permutations in lex order.
- When the counter reaches k, record the current permutation and stop recursion.
- Return the recorded permutation.
💡 The key difficulty is managing the global count and stopping recursion early to save time.
def getPermutation(n, k):
used = [False] * n
path = []
count = [0]
result = ['']
def backtrack():
if len(path) == n:
count[0] += 1
if count[0] == k:
result[0] = ''.join(map(str, path))
return
if result[0]:
return
for i in range(n):
if not used[i]:
used[i] = True
path.append(i + 1)
backtrack()
path.pop()
used[i] = False
backtrack()
return result[0]
# Driver code
if __name__ == '__main__':
print(getPermutation(3, 3)) # Output: '213'
Line Notes
count = [0]Tracks how many permutations have been generated so far
result = ['']Stores the k-th permutation once found
if len(path) == n:Base case: full permutation formed
count[0] += 1Increment count when a permutation is completed
if count[0] == k:Check if current permutation is the k-th
result[0] = ''.join(map(str, path))Save the k-th permutation
if result[0]:Stop recursion early if k-th permutation found
for i in range(n):Try each number from 1 to n
used[i] = TrueMark number as used
path.append(i + 1)Add number to current path
backtrack()Recurse deeper
path.pop()Backtrack by removing last number
used[i] = FalseMark number as unused
import java.util.*;
public class Solution {
List<String> res = new ArrayList<>();
boolean[] used;
int n, count = 0;
String result = "";
public String getPermutation(int n, int k) {
this.n = n;
used = new boolean[n];
backtrack(new StringBuilder(), k);
return result;
}
private void backtrack(StringBuilder path, int k) {
if (path.length() == n) {
count++;
if (count == k) {
result = path.toString();
}
return;
}
if (!result.isEmpty()) return;
for (int i = 0; i < n; i++) {
if (!used[i]) {
used[i] = true;
path.append(i + 1);
backtrack(path, k);
path.deleteCharAt(path.length() - 1);
used[i] = false;
}
}
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.getPermutation(3, 3)); // Output: 213
}
}
Line Notes
int count = 0Tracks how many permutations generated
String result = ""Stores the k-th permutation once found
if (path.length() == n)Base case: full permutation formed
count++Increment count when a permutation is completed
if (count == k)Check if current permutation is the k-th
result = path.toString()Save the k-th permutation
if (!result.isEmpty()) returnStop recursion early if k-th permutation found
for (int i = 0; i < n; i++)Try each number from 1 to n
used[i] = trueMark number as used
path.append(i + 1)Add number to current path
backtrack(path, k)Recurse deeper
path.deleteCharAt(path.length() - 1)Backtrack by removing last number
used[i] = falseMark number as unused
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Solution {
public:
vector<bool> used;
int n, count = 0;
string result = "";
void backtrack(string &path, int k) {
if ((int)path.size() == n) {
count++;
if (count == k) {
result = path;
}
return;
}
if (!result.empty()) return;
for (int i = 0; i < n; i++) {
if (!used[i]) {
used[i] = true;
path.push_back('1' + i);
backtrack(path, k);
path.pop_back();
used[i] = false;
}
}
}
string getPermutation(int n, int k) {
this->n = n;
used.assign(n, false);
string path = "";
backtrack(path, k);
return result;
}
};
int main() {
Solution sol;
cout << sol.getPermutation(3, 3) << endl; // Output: 213
return 0;
}
Line Notes
int count = 0Tracks how many permutations generated
string result = ""Stores the k-th permutation once found
if ((int)path.size() == n)Base case: full permutation formed
count++Increment count when a permutation is completed
if (count == k)Check if current permutation is the k-th
result = pathSave the k-th permutation
if (!result.empty()) returnStop recursion early if k-th permutation found
for (int i = 0; i < n; i++)Try each number from 1 to n
used[i] = trueMark number as used
path.push_back('1' + i)Add number to current path
backtrack(path, k)Recurse deeper
path.pop_back()Backtrack by removing last number
used[i] = falseMark number as unused
function getPermutation(n, k) {
const used = new Array(n).fill(false);
const path = [];
let count = 0;
let result = '';
function backtrack() {
if (path.length === n) {
count++;
if (count === k) {
result = path.join('');
}
return;
}
if (result) return;
for (let i = 0; i < n; i++) {
if (!used[i]) {
used[i] = true;
path.push(i + 1);
backtrack();
path.pop();
used[i] = false;
}
}
}
backtrack();
return result;
}
// Test
console.log(getPermutation(3, 3)); // Output: 213
Line Notes
let count = 0Tracks how many permutations generated
let result = ''Stores the k-th permutation once found
if (path.length === n)Base case: full permutation formed
count++Increment count when a permutation is completed
if (count === k)Check if current permutation is the k-th
result = path.join('')Save the k-th permutation
if (result) returnStop recursion early if k-th permutation found
for (let i = 0; i < n; i++)Try each number from 1 to n
used[i] = trueMark number as used
path.push(i + 1)Add number to current path
backtrack()Recurse deeper
path.pop()Backtrack by removing last number
used[i] = falseMark number as unused
In worst case, we generate up to k permutations, each of length n, so O(k * n).
💡 If k is large (close to n!), this is still very slow and impractical.
Interview Verdict: TLE for large k
Early stopping helps but still too slow for large inputs; motivates direct computation.