子集树和排列树

假设现在有一列数a[0],a[1], ...a[n-1]

①如果一个问题的解的长度不是固定的,并且解和元素顺序无关,即可以从中选择0个或多个,那么解空间的个数将是指数级别的,为2^n,可以用下面的子集树来表示所有的解

子集树的算法框架为:

void backtrack(int t) {//表示访问到第t层,t从0开始
if (t == n)
 output(x);
else
for (int i = 0; i <= l; i++) { //表示选或不选a[t]
  x[t] = i;
  if (constraint(t) && bound(t))
   backtrack(t + 1);
 }
}

②如果解空间是由n个元素的排列形成,也就是说n个元素的每一个排列都是解空间中的一个元素,那么,最后解空间的组织形式是排列树

排列树算法的基本框架为:

void backtrack(int t)
{
    if (t == n)
        output(x);
    else
        for (int i = t; i < n; i++)
        {
            swap(x[t], x[i]);                    1
            if (constraint(t) && bound(t))
            {
                backtrack(t + 1);                2
                swap(x[t], x[i]);                3
            }
        }
}

遇到子集树和排列数的回溯问题,几乎都可以用上面的模板来套。

注意: 序列A表示“前缀”序列,以便输出,S表示需要进行全排列的元素集合,以便依次选做第一个元素。

void print_emu(序列 A, 集合S) {
  if (序列A满了) 输出序列A;
 else {
  1.按照从小到大依次考虑S中的每一个元素v
  2.print_emu(在A的末尾加上v后得到新的序列, S-{v})
 }
}

以n=3 为例说明递归的执行过程:初始化x[]={1,2,3}

开始时:t=1,n=3;

  进入Backtrack(1) 此时t=1,i=1,执行①此时x[1]=1与x[1]=1交换;

  然后执行②进入t=1,i=1 的Backtrack(2)执行t=2,i=2的①x[2]=2与x[2]=2交换;

  然后执行②进入t=2,i=2的Backtrack(3)执行t=3,i=3的①x[3]=3与x[3]=3交换;

  然后执行②进入Backtrack(4)输出x序列(1,2,3)。

  返回到Backtrack(3)层执行t=3,i=3的③x[3]=3与x[3]=3(即还原成原序列),返回到t=2,i=2的Backtrack(2)层执行③x[2]=2与x[2]=2交换,至此t=2,i=2执行完成。

  下面开始执行t=2,i=3执行①此时x[2]=2与x[3]=3交换;

  然后执行②进入t=2,i=3的Backtrack(3)执行t=3,i=3 的①x[3]=3与x[3]=3交换;

  然后执行②进入Backtrack(4)输出x序列(1,3,2)。

  返回到Backtrack(3)层执行t=3,i=3的③x[3]=3与x[3]=3(即还原成原序列1,3,2),返回到t=2,i=2的Backtrack(2)层执行③x[2]=3与x[3]=2(即还原成原序列1,2,3)交换,至此t=2,i=3执行完成。

至此t=1,i=1执行完成,

下面关于执行t=1,i=2和t=1,i=3的过程与t=1,i=1类似。

时间: 2024-08-02 19:21:36

子集树和排列树的相关文章

回溯之子集树和排列树(子集和问题)

一.子集树          子集树:当所给的问题是从n个元素的集合S中找出满足某种性质的子集时,相应的解空间称为子集树.例如,那个物品的0-1背包问题所相应的解空间树就是一颗子集树.这类子集问题通常有2^n个叶节点,其节点总个数为2^(n+1)-1.遍历子集树的任何算法均需要O(2^n)的计算时间. \ void backtrack (int t) { if (t>n) output(x); else for (int i=0;i<=1;i++) { x[t]=i; if (legal(t)

两种典型的解空间树:子集树和排列树

(1)定义 子集树:所给问题是从N个元素的集合中找出满足某种性质的子集时,相应的解空间树,称为子集树.子集树通常有2^n个叶节点,遍历子集树的任何算法都需要O(2^n)的计算时间. 例如:0-1背包问题的解空间树为一棵子集树. 排列树:当所给的问题是确定N个元素满足某种性质的排列时,相应的解空间称为排列树,排列树通常有N!个叶节点,因此,遍历排列树需要N!的计算时间. 例如:旅行售货员问题的解空间树为一棵排列树. (2)回溯法遍历实现 1.搜索子集树 1 void backtrack (int

排列树与子集树

子集树 子集树主要求解当前集合的所有子集,是算法中的基本思想指导.参考代码如下,以求解字符串的子集串为例子: 1 /** 2 * 子集树 3 * @param S 字符数组 4 * @param t 初始为0 5 */ 6 public void ZJTree(char[] S , int t){ 7 if(t > S.length - 1){ // 叶子节点 8 System.out.println(String.valueOf(S).replace("#","&qu

回溯法 子集树和排序树

当所给问题是从n个元素的集合S中找出满足某种性质的子集时,解空间为子集树.例如:0-1背包问题 当所给问题是从n个元素的集合S中找出满足某种性质的排列时,解空间为排列树.例如:旅行售货员问题 回溯法搜索子集树算法描述为: void backtrack(int  t) { if(t>n) output(x); else for(int i=0; i<=1; i++) { x[t] = i; if(constraint(t) && bound(t)) backtrack(t+1);

排列树

还是拿背包问题来分析吧,假设有N个背包,现在我们要对这个N个背包排列..输出所有情况 那么有:          背包                   1                   2                   3                   ...                   n                         (n个背包选一个!)      (n-1个背包选一个!)      (n-2个背包选一个!)       ...     (只剩

跳跃表,字典树(单词查找树,Trie树),后缀树,KMP算法,AC 自动机相关算法原理详细汇总

第一部分:跳跃表 本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈"跳跃表"的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代码,以及本人对其的了解.难免有错误之处,希望指正,共同进步.谢谢. 跳跃表(Skip List)是1987年才诞生的一种崭新的数据结构,它在进行查找.插入.删除等操作时的期望时间复杂度均为O(logn),有着近乎替代平衡树的本领.而且最重要的一点,就是它的编程复杂度较同类

【经典数据结构】B树与B+树

本文转载自:http://www.cnblogs.com/yangecnu/p/Introduce-B-Tree-and-B-Plus-Tree.html 维基百科对B树的定义为“在计算机科学中,B树(B-tree)是一种树状数据结构,它能够存储数据.对其进行排序并允许以O(log n)的时间复杂度运行进行查找.顺序读取.插入和删除的数据结构.B树,概括来说是一个节点可以拥有多于2个子节点的二叉查找树.与自平衡二叉查找树不同,B-树为系统最优化大块数据的读和写操作.B-tree算法减少定位记录时

K-th Number 线段树(归并树)+二分查找

K-th Number 题意:给定一个包含n个不同数的数列a1, a2, ..., an 和m个三元组表示的查询.对于每个查询(i, j, k), 输出ai, ai+1, ... ,aj的升序排列中第k个数 . 题解:用线段树,每个节点维护一个区间并且保证内部升序,对于每次查询x,返回该区间小于x的数的个数.就这样不断二分,直到找到x为止. 线段树(归并树)+二分查找 1 #include <iostream> 2 #include <cstdio> 3 #include <

B树、Trie树详解

查找(二) 散列表 散列表是普通数组概念的推广.由于对普通数组可以直接寻址,使得能在O(1)时间内访问数组中的任意位置.在散列表中,不是直接把关键字作为数组的下标,而是根据关键字计算出相应的下标. 使用散列的查找算法分为两步.第一步是用散列函数将被查找的键转化为数组的一个索引. 我们需要面对两个或多个键都会散列到相同的索引值的情况.因此,第二步就是一个处理碰撞冲突的过程,由两种经典解决碰撞的方法:拉链法和线性探测法. 散列表是算法在时间和空间上作出权衡的经典例子. 如果没有内存限制,我们可以直接