package com.freetymekiyan.algorithms.level.medium; import org.junit.Test; import java.util.ArrayList; import java.util.List; /** * Given two integers n and k, return all possible combinations of k numbers * out of 1 ... n. * <p> * For example, * If n = 4 and k = 2, a solution is: * | [ * | [2,4], * | [3,4], * | [2,3], * | [1,2], * | [1,3], * | [1,4], * | ] * <p> * Tags: Backtracking */ public class Combinations { /** * Backtracking. * From 1 to n. */ public List<List<Integer>> combine(int n, int k) { List<List<Integer>> res = new ArrayList<>(); backtrack(res, n, k, 1, new ArrayList<>()); return res; } /** * Stop when k is 0, meaning that all k numbers are picked. * | Add combination to result. * For each i from start to n: * | Add i to current combination that means picked. * | Backtrack to pick k-1 numbers from i + 1 to n. * | Reset by removing last picked value from combination. */ private void backtrack(List<List<Integer>> res, int n, int k, int start, List<Integer> comb) { if (k == 0) { res.add(new ArrayList<>(comb)); return; } for (int i = start; i <= n; i++) { comb.add(i); backtrack(res, n, k - 1, i + 1, comb); comb.remove(comb.size() - 1); } } /** * Backtracking. * From n to 1. */ public List<List<Integer>> combineB(int n, int k) { List<List<Integer>> res = new ArrayList<>(); combineB(n, k, new ArrayList<>(), res); return res; } /** * DFS. * Base case: * If k is 0, add the combination. * If n <= k, add all n numbers. * Recurrence relation: * Final combination consists of two parts: * 1. Pick current number, pick k - 1 numbers from n - 1 to 1. * 2. Don't pick current number, pick k numbers from n - 1 to 1. */ private void combineB(int n, int k, List<Integer> comb, List<List<Integer>> result) { if (k == 0) { result.add(comb); return; } if (n <= k) { // Choose all. for (int i = n; i > 0; i--) { comb.add(i); } result.add(comb); return; } // With n, choose k-1 from n-1. List<Integer> combWithN = new ArrayList<>(comb); combWithN.add(n); combineB(n - 1, k - 1, combWithN, result); // Without n, choose k from n-1. combineB(n - 1, k, comb, result); } @Test public void testExamples() { List<List<Integer>> lists = combine(4, 2); for (List<Integer> l : lists) { System.out.println(l.toString()); } } }