Permutation Tree Examples:
class Solution:
def perm(self, nums):
l = len(nums)
res = []
def dfs(depth,arr):
nonlocal l,nums,res
if (l == depth):
res.append(list(nums))
return
for i in range(depth, l): #从depth开始
arr[i],arr[depth] = arr[depth],arr[i]
dfs(depth+1,arr) #写成dfs(depth+1,list(arr)),拷贝构造一份,结果会出错
arr[i],arr[depth] = arr[depth],arr[i] #没有这一句,无法按照字典序进行
dfs(0,nums)
return res
s = Solution()
print(s.perm([1,2,3]))
If nums contain duplicates, use set in the same layer:
class Solution:
def permuteUnique1(self, nums):
nums.sort()
res, l = [], len(nums)
def dfs(depth):
if (depth == l):
res.append(list(nums))
return
vis = set() # 对这一层去重即可
for i in range(depth, l):
if (nums[i] not in vis):
vis.add(nums[i])
nums[depth], nums[i] = nums[i], nums[depth]
dfs(depth + 1)
nums[depth], nums[i] = nums[i], nums[depth]
dfs(0)
return res
def permuteUnique(self, nums):
nums.sort()
res = [list(nums)]
def next_perm(arr):
l = len(arr)
ii = -1
for i in range(l - 1, 0, -1):
if (arr[i - 1] < arr[i]):
ii = i - 1
break
if (ii >= 0): # bug1: 老写成ii > 0
jj = ii + 1
for j in range(ii + 1, l):
if (arr[ii] < arr[j] and arr[j] <= arr[jj]): # bug2: 注意是<=
jj = j
arr[ii], arr[jj] = arr[jj], arr[ii]
arr[ii + 1:] = arr[ii + 1:][::-1] # bug3: arr = arr[::-1]并不会逆转,arr[:]=arr[::-1]
return arr
else:
return None
while (True):
nums = next_perm(nums)
if (nums != None):
res.append(list(nums))
else:
return res
s = Solution()
# print(s.permuteUnique([1,1,2]))
# print(s.permuteUnique([1,1,2]))
print(s.permuteUnique([0, 0, 0, 1, 9]))
Subset Tree Examples:
Backtracks are usually written as recursive programs, the general solution for subset trees is :
void backtrack(int t) {
if (t > n)
output(x);
else
for (int i = f(n, t); i <= g(n,t); ++i) {
x[t] = h(i);
if (constraint(t) && bound(t))
backtrack(t + 1);
}
}
t stands for the recursive depth, f(n, t) is the starting number of next layer, g(n, t) is the ending number of next layer. Bound is a function for pruning.
But sometimes, non-recursive programs are essential:
void iterativeBacktrack() {
int t = 0;
while (t > -1) {
if (t == n && solution(t))
output(x);
if (f(n, t) + 1 <= g(n, t)){
f(n, t) = f(n, t) + 1;
if (constraint(t) && bound(t))
++t;
}
}
else
--t;
}
}
An example:
问题:给出一些数目,可以用加减乘除计算结果,求一些满足条件的结果。例如算24点。
简化:生成+-*/的所有可能计算方式。(貌似不是数学中的排列,也不是数学中的组合)
求解:(非递归)回溯法。
#include <iostream>
using namespace std;
// 0=>+, 1=>-, 2=>*, 3=>/
int op[100];
int cnt = 0;
void output(int n) {
for(int i = 0; i <= n-1; i++) {
switch (op[i]) {
case 0:
cout << "+";
break;
case 1:
cout << "-";
break;
case 2:
cout << "*";
break;
case 3:
cout << "/";
break;
default:
cout << "error";
break;
}
}
cout << endl;
}
void back_tracking_2(int n)
{
memset(op, -1, sizeof(op));
int i = 0;
while (i >-1) {
if (i == n) {
cnt++;
output(n);
i--;
}
// -1指示其状态为:未确定。
// (0,1,2,3), 4 如有下一个解
if (op[i]+1 < 4) {
op[i]++; // 尝试下一个解
i++; // 前进,扩展分支
}
else {
op[i] = -1; // 重设分支的状态为未定
i--; // 回溯
}
}
}
int main()
{
back_tracking_2(2);
cout << cnt << " solutions found!" << endl;
return 0;
}
Another example for subset tree:
Given a collection of integers that might contain duplicates, S, return all possible subsets.
Note:
- Elements in a subset must be in non-descending order.
- The solution set must not contain duplicate subsets.
For example,
If S = [1,2,2]
, a solution is:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
class Solution {
public:
vector<vector<int>> subsetsWithDup(vector<int> &S)
{ // Note: The Solution object is instantiated only once and is reused by each test case.
vector<vector<int> > res;
vector<int> numset;
int start = 0, len = S.size(), i, j;
vector<int> status(len, -1);
res.push_back(numset);
sort(S.begin(), S.end());
while (start > -1)
{
if (start == len)
--start;
if (status[start] == -1)
{
++status[start++];
}
else if (status[start] == 0)
{
++status[start];
numset.push_back(S[start++]);
res.push_back(numset);
}
else
{
status[start] = -1;
numset.pop_back();
--start;
}
}
sort(res.begin(), res.end());
res.resize(distance(res.begin(), unique(res.begin(), res.end())));
return res;
}
};
However, removing the duplicates is complicated here. The key point is that getting back to the former layer is determined by --start, we must record the former position manually.
Python Version:
class Solution:
def subsetsWithDup(self, nums):
res,cur = [[]],[]
numsDict = {}
for num in nums:
if num in numsDict:
numsDict[num] += 1
else:
numsDict[num] = 1
uniqueKeys = list(numsDict.keys())
uniqueKeysLen = len(uniqueKeys)
status = [-1 for i in range(uniqueKeysLen)]
t = 0
while t > -1:
if (t == uniqueKeysLen):
t -= 1
elif (status[t] == -1):
status[t] = 0
t += 1
elif (status[t] > -1 and status[t] < numsDict[uniqueKeys[t]]):
cur.append(uniqueKeys[t])
res.append(list(cur))
status[t] += 1
t += 1
elif (status[t] == numsDict[uniqueKeys[t]]):
for i in range(numsDict[uniqueKeys[t]]):
cur.pop()
status[t] = -1
t -= 1
return res
if __name__ == '__main__':
s = Solution()
res = s.subsetsWithDup([1,2,2])
print(res)
Recursive Version:
class Solution:
def Backtrack(self, numsDict, s, res, cur, uniqueKeys, uniqueKeysLen):
if (s == uniqueKeysLen):
return
curList = list(cur)
self.Backtrack(numsDict, s+1, res, curList, uniqueKeys, uniqueKeysLen)
for i in range(numsDict[uniqueKeys[s]]):
curList.append(uniqueKeys[s])
res.append(list(curList))
self.Backtrack(numsDict, s+1, res, curList, uniqueKeys, uniqueKeysLen)
def subsetsWithDup(self, nums):
res,cur = [[]],[]
numsDict = {}
for num in nums:
if num in numsDict:
numsDict[num] += 1
else:
numsDict[num] = 1
uniqueKeys = list(numsDict.keys())
uniqueKeysLen = len(uniqueKeys)
self.Backtrack(numsDict, 0, res, cur, uniqueKeys, uniqueKeysLen)
return res
if __name__ == '__main__':
s = Solution()
res = s.subsetsWithDup([1,2])
print(res)