Problem
sub-problem-1
base-problem
sub-problem-2
sub-problem-3
sub-sub-problem-2
sub-sub-sub-problem
sub-sub-problem-1
...
...
...
Problem
sub-problem-1
sub-sub-problem
sub-sub-sub-problem
base-problem
Problem
sub-problem-1
base-problem
sub-problem-2
sub-problem-3
sub-sub-problem-2
sub-sub-sub-problem
sub-sub-problem-1
...
...
...
Problem
sub-problem-1
sub-sub-problem
sub-sub-sub-problem
base-problem
Problem
sub-problem-1
base-problem
sub-problem-2
sub-problem-3
sub-sub-problem-2
sub-sub-sub-problem
sub-sub-problem-1
...
...
...
Problem
sub-problem-1
sub-sub-problem
sub-sub-sub-problem
base-problem
Problem
sub-problem-1
base-problem
sub-problem-2
sub-problem-3
sub-sub-problem-2
sub-sub-sub-problem
sub-sub-problem-1
...
...
...
Problem
sub-problem-1
sub-sub-problem
sub-sub-sub-problem
base-problem
Problem
sub-problem-1
base-problem
sub-problem-2
sub-problem-3
sub-sub-problem-2
sub-sub-sub-problem
sub-sub-problem-1
...
...
...
Problem
sub-problem-1
sub-sub-problem
sub-sub-sub-problem
base-problem
Problem
sub-problem-1
sub-sub-problem
sub-sub-sub-problem
base-problem
Problem
sub-problem-1
base-problem
sub-problem-2
sub-problem-3
sub-sub-problem-2
sub-sub-sub-problem
sub-sub-problem-1
...
...
...
Backtracking
Recursion with 1 sub problem does not have to use recursion
We can think about using iteration instead
int Factorial(int n, int total) {
if(n == 1) return 1;
return n * Factorial(n-1, n * total);
}
int Factorial(int n) {
int sum = 1;
for(int i = 1; i <= n; i ++) {
sum = sum * i;
}
return sum;
}
def factorial(n):
if n == 1:
return total
return n * factorial(n-1)
def factorial(n):
sum = 1
for i in range(1, n+1):
sum = sum * i
return sum
The gray code is a binary numeral system where two successive values differ in only one bit.
Given a non-negative integer n representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0.
For example, given n = 2, return [0,1,3,2]
This is a typical problem with only one sub-problem
You need to find the pattern inside of it.
What is gray code: a form of binary that uses a different method of incrementing from one number to the next
Example: 000 001 011 010 110 111 101 100
0 1 3 2 6 7 5 4
The gray code is a binary numeral system where two successive values differ in only one bit.
Given a non-negative integer n representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0.
For example, given n = 2, return [0,1,3,2]
n=2
0
1
3
2
n=3
0
1
3
2
6
7
5
4
Symmetric
public List<Integer> grayCode(int n) {
List<Integer> result = new ArrayList<>();
helper(n, result);
return result;
}
public void helper(int n, List<Integer> result) {
if (n == 0) {
result.add(0);
return;
}
helper(n-1, result);
int size = result.size();
int k = 1 << (n - 1);
for (int i = size - 1; i >= 0; i --) {
result.add(result.get(i) + k);
}
return;
}
The key is to find the relationship between each sub-problem
hold the result
The key is to find the relationship between each sub-problem
def gray_code(n):
result = []
helper(n, result)
return result
def helper(n, result):
if n == 0:
result.append(0)
return
helper(n-1, result)
size = len(result)
k = 1 << (n - 1)
for i in range(size - 1, -1, -1):
result.append(result[i] + k)
return
def grayCode(self, n: int) -> List[int]:
if n == 0:
return [0]
res = self.grayCode(n - 1)
# add = 2 ** (n - 1) # this also works
add = 1 << (n - 1)
for i in range(len(res) - 1, -1, -1):
res.append(res[i] + add)
return res
public List<Integer> grayCode(int n) {
List<Integer> result = new ArrayList<>();
result.add(0);
for(int i = 0; i < n; i ++) {
int k = 1 << i;
int size = result.size();
for(int j = size - 1; j >= 0; j --) {
result.add(result.get(j) + k);
}
}
return result;
}
We actually could get rid of the recursion code.
Should we use recursion or iteration?
Same as recursion, why?
Implement pow(x, n), which calculates x
raised to the power n
(i.e., xn
).
Input: x = 2.00000, n = 10
Output: 1024.00000
Input: x = 2.10000, n = 3
Output: 9.26100
Input: x = 2.00000, n = -2
Output: 0.25000
Explanation: 2-2 = 1/22 = 1/4 = 0.25
def myPow(self, x: float, n: int) -> float:
if n == 0:
return 1
if n == 1:
return x
if n < 0:
return self.myPow(1 / x, -n)
if n % 2:
return self.myPow(x * x, n // 2) * x
else:
return self.myPow(x * x, n // 2)
Given a maze and a start point and a target point, return whether the target can be reached.
Example Input:
Start Point: (0, 0); Target Point (5, 5);
Maze: char[][] = {
{'.', 'X', '.', '.', '.', 'X'},
{'.', '.', '.', 'X', '.', 'X'},
{'X', 'X', '.', 'X', '.', '.'},
{'.', 'X', 'X', 'X', '.', 'X'},
{'.', '.', '.', '.', '.', 'X'},
{'.', '.', '.', '.', '.', '.'}
}
Example Output: True
Given a maze and a start point and a target point, return whether the target can be reached.
Example Input:
Start Point: (0, 0); Target Point (5, 5);
Maze: char[][] = {
{'.', 'X', '.', '.', '.', 'X'},
{'.', '.', '.', 'X', '.', 'X'},
{'X', 'X', '.', 'X', '.', '.'},
{'.', 'X', 'X', 'X', '.', 'X'},
{'.', '.', '.', '.', '.', 'X'},
{'.', '.', '.', '.', '.', '.'}
}
Example Output: True
Given a maze and a start point and a target point, return whether the target can be reached.
Consider what are valid subproblem?
What direction can it go?
Given a maze and a start point and a target point, return whether the target can be reached.
Given a maze and a start point and a target point, return whether the target can be reached.
Given a maze and a start point and a target point, return whether the target can be reached.
public static boolean solveMaze(char[][] maze,
int startX, int startY, int targetX, int targetY,
boolean[][] visited) {
if (startX < 0 || startX >= maze.length ||
startY < 0 || startY >= maze[0].length ||
maze[startX][startY] == 'X' || visited[startX][startY]) {
return false;
}
if (startX == targetX && startY == targetY) {
return true;
}
visited[startX][startY] = true;
if (solveMaze(maze, startX + 1, startY, targetX, targetY, visited) ||
solveMaze(maze, startX, startY + 1, targetX, targetY, visited) ||
solveMaze(maze, startX - 1, startY, targetX, targetY, visited) ||
solveMaze(maze, startX, startY - 1, targetX, targetY, visited)) {
return true;
}
return false;
}
Given a maze and a start point and a target point, return whether the target can be reached.
Does the base case order matter?
Given a maze and a start point and a target point, return whether the target can be reached.
def solve_maze(maze, startX, startY, targetX, targetY, visited):
if (startX < 0 or startX >= len(maze) or
startY < 0 or startY >= len(maze[0]) or
maze[startX][startY] == 'X' or visited[startX][startY]):
return False
if startX == targetX and startY == targetY:
return True
visited[startX][startY] = True
if (solve_maze(maze, startX + 1, startY, targetX, targetY, visited) or
solve_maze(maze, startX, startY + 1, targetX, targetY, visited) or
solve_maze(maze, startX - 1, startY, targetX, targetY, visited) or
solve_maze(maze, startX, startY - 1, targetX, targetY, visited)):
return True
return False
public static boolean solveMaze(char[][] maze,
int startX, int startY, int targetX, int targetY) {
if (startX < 0 || startX >= maze.length ||
startY < 0 || startY >= maze[0].length ||
maze[startX][startY] == 'X') {
return false;
}
if (startX == targetX && startY == targetY) {
return true;
}
maze[startX][startY] = 'X';
if (solveMaze(maze, startX + 1, startY, targetX, targetY) ||
solveMaze(maze, startX, startY + 1, targetX, targetY) ||
solveMaze(maze, startX - 1, startY, targetX, targetY) ||
solveMaze(maze, startX, startY - 1, targetX, targetY)) {
return true;
}
return false;
}
Given a maze and a start point and a target point, return whether the target can be reached.
Optimization 1: reuse the maze to reduce the space complexity
Given a maze and a start point and a target point, return whether the target can be reached.
Optimization 1: reuse the maze to reduce the space complexity
def solve_maze(maze, startX, startY, targetX, targetY):
if (startX < 0 or startX >= len(maze) or
startY < 0 or startY >= len(maze[0]) or
maze[startX][startY] == 'X'):
return False
if startX == targetX and startY == targetY:
return True
maze[startX][startY] = 'X'
if (solve_maze(maze, startX + 1, startY, targetX, targetY) or
solve_maze(maze, startX, startY + 1, targetX, targetY) or
solve_maze(maze, startX - 1, startY, targetX, targetY) or
solve_maze(maze, startX, startY - 1, targetX, targetY)):
return True
return False
public static boolean solveMaze(char[][] maze,
int startX, int startY, int targetX, int targetY) {
if (startX < 0 || startX >= maze.length ||
startY < 0 || startY >= maze[0].length ||
maze[startX][startY] == 'X') {
return false;
}
if (startX == targetX && startY == targetY) {
return true;
}
maze[startX][startY] = 'X';
int[] dx = {1, 0, -1, 0};
int[] dy = {0, 1, 0, -1};
for (int i = 0; i < 4; i++) {
if (solveMaze(maze, startX + dx[i], startY + dy[i], targetX, targetY)) {
return true;
}
}
return false;
}
Given a maze and a start point and a target point, return whether the target can be reached.
Optimization 2: code style
Given a maze and a start point and a target point, return whether the target can be reached.
Optimization 2: code style
def solve_maze(maze, startX, startY, targetX, targetY):
if (startX < 0 or startX >= len(maze) or
startY < 0 or startY >= len(maze[0]) or
maze[startX][startY] == 'X'):
return False
if startX == targetX and startY == targetY:
return True
maze[startX][startY] = 'X'
dx = [1, 0, -1, 0]
dy = [0, 1, 0, -1]
for i in range(4):
if solve_maze(maze, startX + dx[i], startY + dy[i], targetX, targetY):
return True
return False
public static boolean solveMaze(char[][] maze,
int startX, int startY, int targetX, int targetY) {
if (startX == targetX && startY == targetY) {
return true;
}
maze[startX][startY] = 'X';
int[] dx = {1, 0, -1, 0};
int[] dy = {0, 1, 0, -1};
for (int i = 0; i < 4; i++) {
int newX = startX + dx[i], newY = startY + dy[i];
if (newX < 0 || newX >= maze.length || newY < 0 || newY >= maze.length ||
maze[newX][newY] == 'X') {
continue;
}
if (solveMaze(maze, newX, newY, targetX, targetY)) {
return true;
}
}
return false;
}
Given a maze and a start point and a target point, return whether the target can be reached.
Optimization 3: move the base case -> is it good?
Given a maze and a start point and a target point, return whether the target can be reached.
Optimization 3: move the base case -> is it good?
def solve_maze(maze, startX, startY, targetX, targetY):
if startX == targetX and startY == targetY:
return True
maze[startX][startY] = 'X'
dx = [1, 0, -1, 0]
dy = [0, 1, 0, -1]
for i in range(4):
newX, newY = startX + dx[i], startY + dy[i]
if (newX < 0 or newX >= len(maze) or
newY < 0 or newY >= len(maze[0]) or
maze[newX][newY] == 'X'):
continue
if solve_maze(maze, newX, newY, targetX, targetY):
return True
return False
public static boolean solveMaze(char[][] maze,
int startX, int startY, int targetX, int targetY,
String path) {
if (startX < 0 || startX >= maze.length ||
startY < 0 || startY >= maze[0].length ||
maze[startX][startY] == 'X') {
return false;
}
if (startX == targetX && startY == targetY) {
System.out.println(path);
return true;
}
maze[startX][startY] = 'X';
int[] dx = {1, 0, -1, 0};
int[] dy = {0, 1, 0, -1};
char[] direction = {'D', 'R', 'U', 'L'};
for (int i = 0; i < 4; i++) {
String newPath = path + direction[i] + " ";
if (solveMaze(maze, startX+dx[i], startY+dy[i], targetX, targetY, newPath)) {
return true;
}
}
return false;
}
Given a maze and a start point and a target point, print out the path to reach the target.
Given a maze and a start point and a target point, print out the path to reach the target.
def solve_maze(maze, startX, startY, targetX, targetY, path):
if (startX < 0 or startX >= len(maze) or
startY < 0 or startY >= len(maze[0]) or
maze[startX][startY] == 'X'):
return False
if startX == targetX and startY == targetY:
print(path)
return True
maze[startX][startY] = 'X'
dx = [1, 0, -1, 0]
dy = [0, 1, 0, -1]
direction = ['D', 'R', 'U', 'L']
for i in range(4):
newPath = path + direction[i] + " "
if solve_maze(maze, startX + dx[i], startY + dy[i], targetX, targetY, newPath):
return True
return False
public static boolean solveMaze(char[][] maze,
int startX, int startY, int targetX, int targetY,
ArrayList<Character> path) {
if (startX < 0 || startX >= maze.length ||
startY < 0 || startY >= maze[0].length ||
maze[startX][startY] == 'X') {
return false;
}
if (startX == targetX && startY == targetY) {
return true;
}
maze[startX][startY] = 'X';
int[] dx = {1, 0, -1, 0};
int[] dy = {0, 1, 0, -1};
char[] direction = {'D', 'R', 'U', 'L'};
for (int i = 0; i < 4; i++) {
path.add(direction[i]);
if (solveMaze(maze, startX+dx[i], startY+dy[i], targetX, targetY, path)) {
return true;
}
path.remove(path.size()-1);
}
return false;
}
Given a maze and a start point and a target point, return the path to reach the target.
Given a maze and a start point and a target point, return the path to reach the target.
def solve_maze(maze, startX, startY, targetX, targetY, path):
if (startX < 0 or startX >= len(maze) or
startY < 0 or startY >= len(maze[0]) or
maze[startX][startY] == 'X'):
return False
if startX == targetX and startY == targetY:
return True
maze[startX][startY] = 'X'
dx = [1, 0, -1, 0]
dy = [0, 1, 0, -1]
direction = ['D', 'R', 'U', 'L']
for i in range(4):
path.append(direction[i])
if solve_maze(maze, startX + dx[i], startY + dy[i], targetX, targetY, path):
return True
path.pop()
return False
Given a maze and a start point and a target point, print out the path to reach the target.
Given a maze and a start point and a target point, return the path to reach the target.
Compare these 2, what is the difference?
When we share the field, what should we do?
Importance for backtracking -> keep the subproblem has the same state
Given a collection of distinct numbers, return all possible permutations.
For example,
[1,2,3] have the following permutations:
[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1].
public List<List<Integer>> permute(int[] nums) {
// Implement this method.
}
What is the time complexity?
O(n * n!)
Given a collection of distinct numbers, return all possible permutations.
public List<List<Integer>> permute(int[] num) {
List<List<Integer>> results = new ArrayList<List<Integer>>();
permute(results, new ArrayList<Integer>(), num);
return results;
}
public void permute(List<List<Integer>> results,
ArrayList<Integer> cur, int[] num) {
if (cur.size() == num.length) {
results.add(new ArrayList<Integer>(cur));
return;
}
for (int i = 0; i < num.length; i++) {
if (cur.contains(num[i])) {
continue;
}
cur.add(num[i]);
permute(results, cur, num);
cur.remove(cur.size() - 1);
}
}
Which part is not efficient? -> array contains
Given a collection of distinct numbers, return all possible permutations.
Which part is not efficient? -> array contains
def permute(num):
results = []
permute_helper(results, [], num)
return results
def permute_helper(results, cur, num):
if len(cur) == len(num):
results.append(list(cur))
return
for i in num:
if i in cur:
continue
cur.append(i)
permute_helper(results, cur, num)
cur.pop()
public ArrayList<ArrayList<Integer>> permute(int[] num) {
ArrayList<Integer> numList = new ArrayList<Integer>();
for (int i = 0; i < num.length; i++)
numList.add(num[i]);
return permute(new ArrayList<Integer>(), numList);
}
public ArrayList<ArrayList<Integer>> permute(ArrayList<Integer> cur,
ArrayList<Integer> num) {
ArrayList<ArrayList<Integer>> results =
new ArrayList<ArrayList<Integer>>();
if (num.size() == 0) {
results.add(cur);
return results;
}
for (int i = 0; i < num.size(); i++) {
ArrayList<Integer> newCur = new ArrayList<Integer>(cur);
newCur.add(num.get(i));
ArrayList<Integer> newNum = new ArrayList<Integer>(num);
newNum.remove(i);
results.addAll(permute(newCur, newNum));
}
return results;
}
What part is not efficient?
copy the array takes time
def permute(num):
num_list = list(num)
return permute_helper([], num_list)
def permute_helper(cur, num):
results = []
if len(num) == 0:
results.append(cur)
return results
for i in range(len(num)):
new_cur = cur + [num[i]]
new_num = num[:i] + num[i+1:]
results.extend(permute_helper(new_cur, new_num))
return results
def permute(num):
num_list = list(num)
return permute_helper([], num_list)
def permute_helper(cur, num):
print(cur, num)
results = []
if len(num) == 0:
results.append(cur)
return results
for i in range(len(num)):
new_cur = cur + [num[i]]
new_num = num[:i] + num[i+1:]
results.extend(permute_helper(new_cur, new_num))
return results
([], [2, 6, 8])
([2], [6, 8])
([2, 6], [8])
([2, 6, 8], [])
([2, 8], [6])
([2, 8, 6], [])
([6], [2, 8])
([6, 2], [8])
([6, 2, 8], [])
([6, 8], [2])
([6, 8, 2], [])
([8], [2, 6])
([8, 2], [6])
([8, 2, 6], [])
([8, 6], [2])
([8, 6, 2], [])
Given a collection of distinct numbers, return all possible permutations.
public List<List<Integer>> permuteUnique(int[] num) {
List<List<Integer>> results = new ArrayList<List<Integer>>();
Arrays.sort(num);
boolean[] visited = new boolean[num.length];
for (int i = 0; i < visited.length; i++) {
visited[i] = false;
}
permute(results, new ArrayList<Integer>(), num, visited);
return results;
}
public void permute(List<List<Integer>> results, List<Integer> cur, int[] num, boolean[] visited) {
if (cur.size() == num.length) {
results.add(new ArrayList<Integer>(cur));
return;
}
for (int i = 0; i < num.length; i++) {
if (visited[i] || (i > 0 && num[i] == num[i-1] && !visited[i-1])) {
continue;
}
visited[i] = true;
cur.add(num[i]);
permute(results, cur, num, visited);
cur.remove(cur.size() - 1);
visited[i] = false;
}
return;
}
Given a collection of distinct numbers, return all possible permutations.
def permuteUnique(num):
results = []
num.sort()
visited = [False] * len(num)
permute(results, [], num, visited)
return results
def permute(results, cur, num, visited):
if len(cur) == len(num):
results.append(list(cur))
return
for i in range(len(num)):
if visited[i] or (i > 0 and num[i] == num[i - 1] and not visited[i - 1]):
continue
visited[i] = True
cur.append(num[i])
permute(results, cur, num, visited)
cur.pop()
visited[i] = False
Given a collection of distinct numbers, return all possible permutations.
public List<List<Integer>> permuteUnique(int[] num) {
List<List<Integer>> results = new ArrayList<List<Integer>>();
List<Integer> nums = new ArrayList<Integer>();
Arrays.sort(num);
for (int i = 0; i < num.length; i ++) {
nums.add(num[i]);
}
permute(results, nums, 0);
return results;
}
public void permute(List<List<Integer>> results, List<Integer> num, int index) {
if (index == num.size()) {
results.add(num);
return;
}
for (int i = index; i < num.size(); i++) {
if (i != index && num.get(index) == num.get(i)) continue;
swap(num, index, i);
permute(results, new ArrayList<Integer>(num), index + 1);
}
}
public void swap(List<Integer> num, int i, int j) {
int temp = num.get(i);
num.set(i, num.get(j));
num.set(j, temp);
}
Given a collection of distinct numbers, return all possible permutations.
def permuteUnique(self, nums):
results = []
num = sorted(nums)
self.permute(results, num, 0)
return results
def permute(self, results, num, index):
if index == len(num):
results.append(num[:])
return
for i in range(index, len(num)):
if i != index and num[index] == num[i]:
continue
num[index], num[i] = num[i], num[index]
self.permute(results, num[:], index + 1)
Given a collection of distinct numbers, return all possible combinations.
For example,
[2, 6, 8] have the following powersets (combination):
[], [2], [6], [8], [2, 6], [2, 8], [6, 8], [2, 6, 8].
public List<List<Integer>> combine(int[] nums) {
// Implement this method.
}
Given a collection of distinct numbers, return all possible combinations.
public static List<List<Integer>> combine(int[] nums) {
List<List<Integer>> results = new ArrayList<>();
combination(results, nums, 0, new ArrayList<Integer>());
return results;
}
public static void combination(List<List<Integer>> results,
int[] nums, int index, ArrayList<Integer> items) {
if (index == nums.length) {
results.add(items);
return;
}
ArrayList<Integer> newItems1 = new ArrayList<Integer>(items);
combination(results, nums, index+1, newItems1);
ArrayList<Integer> newItems2 = new ArrayList<Integer>(items);
newItems2.add(nums[index]);
combination(results, nums, index+1, newItems2);
}
Given a collection of distinct numbers, return all possible combinations.
def combine(nums):
results = []
combination_helper(results, nums, 0, [])
return results
def combination_helper(results, nums, index, items):
if index == len(nums):
results.append(list(items))
return
combination_helper(results, nums, index + 1, items)
new_items = items + [nums[index]]
combination_helper(results, nums, index + 1, new_items)
Given a collection of distinct numbers, return all possible combinations.
public static List<List<Integer>> combine(int[] nums) {
List<List<Integer>> results = new ArrayList<>();
combination(results, nums, 0, new ArrayList<Integer>());
return results;
}
public static void combination(List<List<Integer>> results,
int[] nums, int index, ArrayList<Integer> items) {
if (index == nums.length) {
results.add(new ArrayList<Integer>(items));
return;
}
combination(results, nums, index+1, items);
items.add(nums[index]);
combination(results, nums, index+1, items);
items.remove(items.size()-1);
}
Given a collection of distinct numbers, return all possible combinations.
def combine(nums):
results = []
combination_helper(results, nums, 0, [])
return results
def combination_helper(results, nums, index, items):
if index == len(nums):
results.append(list(items))
return
combination_helper(results, nums, index + 1, items)
items.append(nums[index])
combination_helper(results, nums, index + 1, items)
items.pop()
Given a collection of distinct numbers, return all possible combinations.
public static List<List<Integer>> combine(int[] nums) {
List<List<Integer>> results = new ArrayList<>();
combination(results, nums, 0, new ArrayList<Integer>());
return results;
}
public static void combination(List<List<Integer>> results,
int[] nums, int index,
ArrayList<Integer> items) {
if (index == nums.length) {
results.add(new ArrayList<Integer>(items));
return;
}
for (int i = index; i < nums.length; i++) {
items.add(nums[i]);
combination(results, nums, i+1, items);
items.remove(items.size()-1);
}
}
The empty set is missing.
-There will be at least one element in the "items"
input: 2 6 8
output:
8
2,8
6,8
2,6,8
Given a collection of distinct numbers, return all possible combinations.
def combine(nums):
results = []
combination_helper(results, nums, 0, [])
return results
def combination_helper(results, nums, index, items):
results.append(list(items))
if index == len(nums):
return
for i in range(index, len(nums)):
items.append(nums[i])
combination_helper(results, nums, i + 1, items)
items.pop()
Given a collection of distinct numbers, return all possible combinations.
public List<List<Integer>> combine(int[] nums) {
List<List<Integer>> results = new ArrayList<>();
combinationHelper(results, nums, 0, new ArrayList<>());
return results;
}
private void combinationHelper(List<List<Integer>> results, int[] nums, int index, List<Integer> items) {
results.add(new ArrayList<>(items));
if (index == nums.length) {
return;
}
for (int i = index; i < nums.length; i++) {
items.add(nums[i]);
combinationHelper(results, nums, i + 1, items);
items.remove(items.size() - 1);
}
}
888 is a lucky number. And for each American phone number, we can actually add some operators to make it become 888. For example:
phone number is 7765332111, you will have
7/7*65*3+3*21*11 = 888
776+5+3*32+11*1 = 888
...
We want to get a full list of all the operation equations that can get a certain lucky number. The interface will be
List<String> luckyNumbers(String num, int target)
We want to get a full list of all the operation equations that can get a certain lucky number. The interface will be
List<String> luckyNumbers(String num, int target)
Additional information:
String num will always be 10 digits since it is a phone number.
we can have "0" but we cannot have "05", "032".
If a number cannot be divided, you cannot use it. For example, 55/2 is not allowed, you need to make sure the division can always be an integer result.
0 cannot be divided.
We need to use recursion to solve the problem
Since we want to output the result. There are things we need to pay attention:
1. The first number does not have operators in front.
2. Use long in case you get the number overflow
3. * and / has higher priority than + and -. How do you handle that?
4. remind 0
def lucky_numbers(num, target):
result = []
recursion(num, target, "", 0, 0, 0, result)
return result
def recursion(num, target, temp, pos, current, last, result):
if pos == len(num):
if current == target:
result.append(temp)
return
for i in range(pos, len(num)):
if num[pos] == '0' and i != pos:
break
m = num[pos:i + 1]
n = int(m)
if pos == 0:
recursion(num, target, temp + m, i + 1, n, n, result)
else:
recursion(num, target, temp + "+" + m, i + 1, current + n, n, result)
recursion(num, target, temp + "-" + m, i + 1, current - n, -n, result)
recursion(num, target, temp + "*" + m, i + 1, current - last + last * n, last * n, result)
if n != 0 and last % n == 0:
recursion(num, target, temp + "/" + m, i + 1, current - last + last // n, last // n, result)
public List<String> luckyNumbers(String num, int target) {
List<String> result = new ArrayList<>();
recursion(num, target, "", 0, 0, 0, result);
return result;
}
public void recursion(String num, int target, String temp, int pos,
long current, long last, List<String> result) {
if (pos == num.length()) {
if (current == target) {
result.add(temp);
}
return;
}
for (int i = pos; i < num.length(); i ++) {
if (num.charAt(pos) == '0' && i != pos) break;
String m = num.substring(pos, i + 1);
long n = Long.valueOf(m);
if (pos == 0) {
recursion(num, target, temp + m, i + 1, n, n, result);
} else {
recursion(num, target, temp + "+" + m, i + 1, current + n, n, result);
recursion(num, target, temp + "-" + m, i + 1, current - n, -n, result);
recursion(num, target, temp + "*" + m, i + 1, current - last + last * n, last * n, result);
if (n != 0 && last % n == 0) {
recursion(num, target, temp + "/" + m, i + 1, current - last + last / n, last / n, result);
}
}
}
}
Example:
24 + 5 -> current 29, last 5
24 + 5 * 4 -> current 29 - 5 + 20 = 44, last 20
24 + 5 * 4 / 2 -> current 44 - 20 + 10 = 34, last 10
24 + 5 * 4 / 2 - 7 -> current 34 - 7 = 27, last -7
Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
All candidate numbers are unique.
The same repeated number may be chosen from C unlimited number of times.
Note:
Example Input: [7], [2, 3, 6, 7]
Example Output: [[2, 2, 3], [7]]
Similar as the previous Knapsack, consider the subproblems
Define the problem as (C, i, T), then given a number
Why i does not move when we pick?
Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
All candidate numbers are unique.
The same repeated number may be chosen from C unlimited number of times.
public static ArrayList<ArrayList<Integer>> knapsack(int[] candidates,
int target) {
ArrayList<ArrayList<Integer>> results = new ArrayList<ArrayList<Integer>>();
ArrayList<Integer> cur = new ArrayList<Integer>();
knapsack(candidates, 0, target, results, cur);
return results;
}
public static void knapsack(int[] candidates, int index, int target,
ArrayList<ArrayList<Integer>> results,
ArrayList<Integer> cur) {
if (target < 0 || (target != 0 && index == candidates.length)) {
return;
}
if (target == 0) {
results.add(new ArrayList<Integer>(cur));
return;
}
cur.add(candidates[index]);
knapsack(candidates, index, target-candidates[index], results, cur);
cur.remove(cur.size()-1);
knapsack(candidates, index+1, target, results, cur);
}
Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
why do we copy?
Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
def knapsack(candidates, target):
results = []
cur = []
knapsack_helper(candidates, 0, target, results, cur)
return results
def knapsack_helper(candidates, index, target, results, cur):
if target < 0 or (target != 0 and index == len(candidates)):
return
if target == 0:
results.append(cur[:])
return
cur.append(candidates[index])
knapsack_helper(candidates, index, target - candidates[index], results, cur)
cur.pop()
knapsack_helper(candidates, index + 1, target, results, cur)
Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
All candidate numbers are unique.
The same repeated number may be chosen from C unlimited number of times.
Another way to solve the problem?
Recursion does not have to be one way to solve
Iterate all numbers to decide whether to pick the current number.
<=>
Which one of the numbers should I pick as the smallest one in the current set.
Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
public ArrayList<ArrayList<Integer>> knapsack(int[] candidates, int target) {
ArrayList<ArrayList<Integer>> results = new ArrayList<ArrayList<Integer>>();
ArrayList<Integer> cur = new ArrayList<Integer>();
knapsack(candidates, 0, target, results, cur);
return results;
}
public void knapsack(int[] candidates, int index, int target,
ArrayList<ArrayList<Integer>> results,
ArrayList<Integer> cur) {
if (target < 0) {
return;
}
if (target == 0) {
results.add(new ArrayList<Integer>(cur));
return;
}
for (int i = index; i < candidates.length; i++) {
cur.add(candidates[i]);
knapsack(candidates, i, target-candidates[i], results, cur);
cur.remove(cur.size()-1);
}
return;
}
Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
def knapsack(candidates, target):
results = []
cur = []
knapsack_helper(candidates, 0, target, results, cur)
return results
def knapsack_helper(candidates, index, target, results, cur):
if target < 0:
return
if target == 0:
results.append(cur[:])
return
for i in range(index, len(candidates)):
cur.append(candidates[i])
knapsack_helper(candidates, i, target - candidates[i], results, cur)
cur.pop()
return
Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
Candidate numbers may contain duplicate.
Each number in C may only be used once in the combination.
Knapsack I
input: [2,3,7], 9 -> [[2,2,2,3],[2,7],[3,3,3]]
Knapsack II
input: [2,2,3,7], 9 -> [[2,7]]
public List<List<Integer>> knapsack(int[] candidates, int target) {
Arrays.sort(candidates);
List<List<Integer>> results = new ArrayList<List<Integer>>();
List<Integer> cur = new ArrayList<Integer>();
knapsack(candidates, 0, target, results, cur);
return results;
}
public void knapsack(int[] candidates, int index, int target,
List<List<Integer>> results, List<Integer> cur) {
if (target < 0) return;
if (target == 0) {
results.add(new ArrayList<Integer>(cur));
return;
}
for (int i = index; i < candidates.length; i++) {
cur.add(candidates[i]);
knapsack(candidates, i+1, target-candidates[i], results, cur);
cur.remove(cur.size()-1);
while (i < candidates.length-1 && candidates[i] == candidates[i+1]) i++;
}
return;
}
when it is used, need to use the next
Why do we need to skip?
-> avoid duplicate subproblem
def knapsack(candidates, target):
candidates.sort()
results = []
cur = []
knapsack_helper(candidates, 0, target, results, cur)
return results
def knapsack_helper(candidates, index, target, results, cur):
if target < 0:
return
if target == 0:
results.append(cur[:])
return
for i in range(index, len(candidates)):
cur.append(candidates[i])
knapsack_helper(candidates, i + 1, target - candidates[i], results, cur)
cur.pop()
while i < len(candidates) - 1 and candidates[i] == candidates[i + 1]:
i += 1
return
Given a knapsack which can hold s pounds of items, and a set of items with weight w1, w2, ... wn. Try to put items into the pack as many as possible, return the largest weight we can get in the knapsack.
public int knapsack(int s, int[] weights) {
return knapsack(s, weights, 0);
}
public int knapsack(int s, int[] weights, int index) {
if (s == 0 || index == weights.length) {
return 0;
}
if (weights[index] > s) {
return knapsack(s, weights, index+1);
}
return Math.max(knapsack(s, weights, index+1),
weights[index] + knapsack(s-weights[index], weights, index+1));
}
The subproblem's relationship has changed.
This is not the best solution, we can use Dynamic Programming
Given a knapsack which can hold s pounds of items, and a set of items with weight w1, w2, ... wn. Try to put items into the pack as many as possible, return the largest weight we can get in the knapsack.
The subproblem's relationship has changed.
This is not the best solution, we can use Dynamic Programming
def knapsack(s, weights):
return knapsack_recursive(s, weights, 0)
def knapsack_recursive(s, weights, index):
if s == 0 or index == len(weights):
return 0
if weights[index] > s:
return knapsack_recursive(s, weights, index + 1)
return max(knapsack_recursive(s, weights, index + 1),
weights[index] + knapsack_recursive(s - weights[index], weights, index + 1))
Given a knapsack which can hold s pounds of items, and a set of items with weight w1, w2, ... wn. Return whether we can pick specific items so that their total weight s.
Example Input:
s = 20;
w = [14, 8, 7, 5, 3];
Example Output:
True; (8, 7, 5)
We need to think what are the sub problems
Given an item, just two options:
Given a knapsack which can hold s pounds of items, and a set of items with weight w1, w2, ... wn. Return whether we can pick specific items so that their total weight s.
public boolean knapsack(int s, int[] weights) {
return knapsack(s, weights, 0);
}
public boolean knapsack(int s, int[] weights, int index) {
if (s == 0) {
return true;
}
if (s < 0 || index == weights.length) {
return false;
}
return knapsack(s - weights[index], weights, index+1) ||
knapsack(s, weights, index+1);
}
add/remove elements in weights is hard --> add an index and move index
Consider the 2 return, does the order matter?
def knapsack(s, weights):
return knapsack_recursive(s, weights, 0)
def knapsack_recursive(s, weights, index):
if s == 0:
return True
if s < 0 or index == len(weights):
return False
return knapsack_recursive(s - weights[index], weights, index+1) or knapsack_recursive(s, weights, index+1)
Recursion
subproblem
-> problem
subproblem-1 ||
subproblem-2 ||
subproblem-N
-> problem
subproblem-1 &&
subproblem-2 &&
subproblem-N
-> problem
Factorial,
Sum of LinkedList,
Remove Linked List Element, etc.
Maze,
Sudoku,
Most DFS problems, etc.
Knapsack
Permutations,
Combinations,
Eight Queen,
kSum, etc.