Recursive backtrack & Non-recursive backtrack & Subset Tree & Permutation Tree 递归回溯与非递归回溯 排列树与子集树

265 篇文章 1 订阅
231 篇文章 0 订阅

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)

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值