defbacktracking(answer,current_position,N,possible_decisions):#anwer: list that should contain those compatible decisions we previously made. The size of it should be == current_position#N: integer indicates the total number of steps to build our final answer#current_position: Integer indicates the id of the step we are at right now. Starts at 0#possible_decisions: a map that associates the id of a step to a collection of possible decisions we can make for that step.iflen(answer)==N:#base case, 得到了一个想解决的问题的解 ... #应该把它存下来else:for decision in possible_decisions[current_position]:ifis_compatible(answer, decision): answer.add(decision)backtracking(answer, current_position+1, N, possible_decisions) answer.remove(decision)
但是不管哪种方法,在test for compatibility的时候,这个检查都是O(n)的。如果想在这里优化,要么建一个Hashset,要么就复用输入的nums,去分析nums的每一个能放的位置,把能放的数字放进结果集的list里。
高阶 swap-swap,in place
Time: O(n!)*n=O(n!*n)
Space: O(n^2) on call stack, the maximum height is n, and at each level, we create a set with space cost of O(n), so O(n*n)
With Duplicates (Generate all permutations)
Input: [1,2,2]
Output:
[1,2,2]
[2,1,2]
[2,2,1]
Note: [1, 2a, 2b] is the same as [1, 2b, 2a]
As compared to the above, we cannot use the same compatibility test
For the same stage, we could not use the same value twice.
Use a collection to store the choices we have tried so far at a given stage.
For the different stages, a value could be used if and only if the total number of time this value has occurred previously +1 <= # of time this value occurs in the input. Or in other words, the total time previously < # of time in the input.
Subset
From a set of distinct integers (Generate all subsets)
2n
3 important things for solving problem with backtracking:
Is this a multistage decision problem? (get a single subset)
What decision we will make for a given stage? (pick or not pick this number)
Is there any compatibility concern? (no)
Difference between subset and permutation is, for permutation, at each stage we must choose some value, but for subsetting, omitting is also a valid option. The decision made at each stage is thus no longer 'which number to pick', with a total stage of N input; but rather, 'to pick or not' is the decision at each stage.
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
Is this a multistage decision problem? (get a single balanced sequence)
What decision we will make for a given stage? (left or right parentheses)
Is there any compatibility concern? (if there is no left bracket on its left to match the current ), then we cannot put the ) at the current position).
During backtracking, we could record the number of remaining ( and the number of remaining );
or number of used ( and the number of used ). used‘(’==used‘)’时不能放')', 只有在左括号的数量多于右括号时才能加右括号。
Branching Factor, B, Height of the Tree, H; Time Complexity: O(BH)
In a total of K stages, for a given stage, possible answers:
Choose number from 1...n
But!! (Problem is, in the compatibility test, we need to make sure first the number we choose is distinctive from each other; second combination(1,2) and combination (2,1) are the same. How to rule out the second possibility?)
Choose or not choose the corresponding number.
But!! (Problem is, each selection has a different range)
Choose number but with a predefined order. We can do this so that the current number we pick will always be larger than the previous one.
Yeah!!(This way, we don't need to consider compatibility)
def bt(perms, perm, seq):
if len(perm)==len(seq): #做完具体决策
perms.append(perm[:])
return
for num in seq:
if is_compatible(perm, num):
perm.append(num)
bt(perms, perm, seq)
perm.pop(num)
def is_compatible(perm, num):
return num not in perm
def permutations(seq):
perms, perm = [], []
bt(perms, perm, seq)
return perms
def bt(perm, current_position, N, numbers, answers):
if current_position==N:
answers.append(perm[:])
for num in nums:
if num not in perm:
perm.append(num)
bt(perm, current_position+1, N, numbers, answers)
perm.pop()
class Solution(object):
def bt(answers, current_position, N, nums):
if current_position == N:
answers.append(nums[:])
return
for i in xrange(current_position, len(nums)):
nums[current_position], nums[i] = nums[i], nums[current_position]
bt(answers, current_position+1, N, nums)
nums[current_position], nums[i] = nums[i], nums[current_position]
return
def permute(self, nums):
answers = []
bt(answers, 0, len(nums), nums)
return answers
class Solution(object):
def permutations(self, input):
"""
input: string input
return: string[]
"""
# write your solution here
result = []
if not input or len(input)<=1:
return [input]
self.dfs(list(input),0,result)
return result
def dfs(self, array, index, result):
if index==len(array):
result.append(''.join(array))
return
for i in range (index, len(array)):
array[index], array[i] = array[i], array[index]
self.dfs(array, index+1, result)
array[index], array[i] = array[i], array[index]
def bt(answers, permutation, N, nums):
if len(permutation) == N:
answers.append(permutation[:])
return
used=set()
for num in nums:
if permutation.count(num)<nums.count(num) and num not in used:
used.add(num)
permutation.append(num)
bt(answers, permutation, N, nums)
permutation,pop()
return
class Solution(object):
def permuteUnique(self, nums):
answers, permutation = [], []
bt(answers, permutation, len(nums), nums)
return answers
class Solution(object):
def permutations(self, input):
"""
input: string input
return: string[]
"""
# write your solution here
result = []
if not input or len(input)<=1:
return [input]
self.dfs(list(input),0,result)
return result
def dfs(self, array, index, result):
if index==len(array):
result.append(''.join(array))
return
seen = set()
for i in range (index, len(array)):
if array[i] not in seen:
seen.add(array[i])
array[index], array[i] = array[i], array[index]
self.dfs(array, index+1, result)
array[index], array[i] = array[i], array[index]
def bt(subsets, subset, seq, curr_pos):
if curr_pos==len(seq): #做完具体决策
subsets.append(subset[:])
return #如果没有return就index out of bound error 因为到底了不知道得反弹了
# Case 1: pick the number seq[currr_pos] 左边的叉 当前的level这个决策,我加
subset.append(seq[curr_pos])
bt(subsets, subset, seq, curr_pos+1)
subset.pop()#回到当前层,往上backtrack的时候,之前新做的都得撤销了 还原到下这个枝之前的状态
# Case 2: Not pick the number 右边的叉
bt(subsets, subset, seq, curr_pos+1) #既然什么都不加,那回去的时候也不用再减
def subset(seq):
ss, s = [], [] ##ss means all subset s means a single valid subset
bt(ss, s, seq, 0)
return ss
class Solution(object):
def subSets(self, set):
"""
input : String set
return : String[]
"""
# write your solution here
result, curr = [], []
if not set or len(set)<1:
return [set]
self.helper(set, curr, 0, result)
return result
def helper(self, set, curr, index, result):
if index==len(set):
result.append(curr[:])
return
curr.append(set[index])
self.helper(set, curr, index+1, result)
curr.pop()
self.helper(set, curr, index+1, result)
def bt(answers, subset, current_position, N, nums):
if current_stage==N:
answers.append(subset[:])
return
subset.append(nums[current_position])
bt(answers, subset, current_position+1, N, nums)
subset.pop()
i=current_position+1
while i<len(nums) and nums[current_position] == nums[i]:
i+=1
bt(answers, subset, i, N, nums)
return
class Solution(object):
def subsetWithDup(self, nums):
nums.sort()
answers = []
bt(answers, [], 0, len(nums), nums)
return answers
# l, r: the number of remaining parenthesis
class Solution(object):
def validParentheses(self, n):
"""
input: int n
return: string[]
"""
# write your solution here
result, curr = [],[]
self.dfs(result,curr,n,n)
return result
def dfs(self, result, curr, l, r):
if r==0:
result.append(''.join(curr))
if l>0:
curr.append('(')
self.dfs(result, curr, l-1, r)
curr.pop()
if r>l:
curr.append(')')
self.dfs(result, curr, l, r-1)
curr.pop()
return
def bt(combs, comb, lower_bound, n, k):
if len(comb)==k:
combs.append(comb[:])
return
for curr_number in range(lower_bound, n+1):
comb.append(curr_number)
bt(combs, comb, curr_number+1, n, k)
comb.pop()
def get_all_combs(n,k):
combs, comb = [],[]
bt(combs, comb, 1, n, k)
return combs
def bt(answers, comb, n):
if n==1 and len(comb)>1:
answers.append(comb[:])
return
for f in range(2 if not comb else comb[-1], n+1):
if n%f==0:
comb.append(f)
bt(answers, comb, n/f)
comb.pop()
class Solution(object):
def getFactors(self, n):
answers = []
bt(answers, [], n)
return answers
import math
def bt(answers, comb, n):
if len(comb)>0:
answers.append(comb + [n])
for f in range (2 if not comb else comb[-1], int(math.sqrt(n))+1):
if n%f==0:
comb.append(f)
bt(answers, comb, n/f)
comb.pop()
class Solution(object):
def getFactors(self, n):
answers=[]
bt(answers, [], n)
return answers