全排列是一种比较烦人的东西。本文讨论如何在不使用递归的情况下,让全排列结果能够被遍历(也就是实现 Iterator 接口),使得全排列的提供者能随时提供“下一个”全排列结果,直到给出所有的排列。
本文发表于:http://yiding-he.iteye.com
通常全排列是不可排序的,因为不同的遍历方式会按不同的顺序得到结果(如交换法、滚动法等)。我们需要选择一种方式,每次枚举都能得到唯一的结果,绝不重复。因为只有这样的方式才能用来遍历。那就是:
全排列树。想象一下,树中的每个节点就是一个全排列结果,其子节点是将其交换变化而来的。举个例子,
根节点为 (1, 2, 3) 的树,其一级子节点就是 (1, 2, 3) (2, 1, 3) (3, 1, 2) 三个,分别是从排列中的 [0]-[0] 交换、[0]-[1] 交换和 [0]-[2]交换得来的。而二级子节点,排列中的位置 [0] 不变,从 [1] 开始和其他位置交换。(2, 1, 3) 的子节点为 (2, 1, 3) 和 (2, 3, 1) 两个。以此类推到下一级,直到无法交换位置为止。实际上,(1, 2, 3) 的全排列树一共就是三层。
你可能看到了:这里不是有重复的节点了吗?没关系。虽然枝节点有重复,但叶子节点是绝无重复的。我们只需要遍历这棵树的最底层,就能得到所有全排列。
遍历的时候,如何确定“上一个”和“下一个”叶子节点的位置呢?用一个很直观的表示法:每个节点的子节点都从 0 开始标上号,根节点就不用标号了,然后根据叶子节点的路径将标号组合成一个数组,就是每个节点的位置。例如上面那棵树中的叶子节点(2, 1, 3),位置就是 {1, 0}。它的下一个位置就是 {1, 1},再下一个位置是 {2, 0},不信把整个树画出来自己对对看。
实际上,因为叶子节点的位置都是绝对的,我就不一定非要从第一个结果来排起。给出任意一个排列结果,我都可以找到它在这棵树中的位置,然后直接求得下一个位置和下一个位置的排列结果。
基本的思路就是这些,然后就是用代码来实现。至于怎么思考实现的就略去了,下面是代码:
java 代码
使用方法:
java 代码
- SortTree tree = new SortTree(6);
- int counter = 0;
- while (tree.hasNext()) {
- int[] sort = (int[]) tree.next();
- printArray(sort);
- counter++;
- }
-
- System.out.println("一共 " + counter + " 个结果");
代码仅用于检验思路,写法不甚严格。若要实用,最好能对参数加一些判断。
分享到:
相关推荐
C#深度优先遍历实现全排列 C#深度优先遍历实现全排列是指通过C#语言使用深度优先遍历算法来实现全排列的过程。全排列是指对一个集合中的元素进行重新排列,以产生所有可能的排列组合。例如,对于数字1、2、3的...
否则,遍历剩余的元素,将每个元素放到首位,并对剩下的元素进行递归调用。 在MATLAB中,`perms`函数的使用更为简便: ```matlab % 使用内置函数perms perms_vec = perms([1 2 3]); for i = 1:size(perms_vec, 1) ...
DFS搜索案例——寻找全排列。 深度优先遍历图的方法是,从图中某顶点v出发: (1)访问顶点v; (2)依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问; (3)若此时图...
3. **遍历待处理元素**:对于每个未处理的元素(即索引小于数组长度),执行以下操作: - **检查重复**:如果元素已存在于目标排列中,跳过此元素,避免重复排列。 - **插入元素**:将元素添加到目标排列中,并...
在编程领域,全排列是一个经典的算法问题,它涉及到如何生成一组特定数量的元素的所有可能排列。在这个场景中,我们关注的是使用C语言来实现对5个数的全排列。C语言是一种底层、高效的编程语言,适合处理这种计算...
它通过遍历已排序的前k-1个元素,并检查它们是否与当前尝试添加的元素`l`相同来实现。如果所有这些元素都不等于`l`,则返回`true`,表明`l`可以添加到排列中;否则返回`false`。 - `voidfind(intk)`:这是递归函数...
否则,对于当前索引位置,遍历所有未使用的元素,并通过`Swap`函数交换它们的位置,然后递归处理下一个位置,最后再回溯(恢复原始状态)。 这个C#程序会输出所有可能的3个数字的排列,例如:123, 132, 213, 231, ...
如果不是,我们就遍历所有可能的选择,将当前元素与起始位置元素交换,然后对剩下的元素进行递归调用。在每次递归返回后,我们还需要通过回溯操作恢复数组的原始状态,以便后续的选择。 全排列问题的递归解决方案...
- 在函数内部,遍历剩余未排列的数字,每次选择一个数字添加到已排列的部分,然后对剩下的数字进行递归调用。 - 当所有数字都已排列时,输出当前排列,然后回溯到上一步,继续尝试其他可能性。 2. **非递归实现**...
- 遍历当前容器中的所有排列,寻找尚未使用的位置。 - 将当前数字放入找到的空位,生成一个新的排列。 - 将新排列添加到结果集中。 - 回溯:如果所有位置都尝试过了,返回上一层;否则,继续尝试下一个未使用的...
3. 如果`当前位索引`未超过数组长度,遍历数组中从`当前位索引`开始的所有未使用过的数字,将每个数字放在`当前位索引`的位置,然后递归调用`全排列`函数处理下一位。 4. 在递归调用返回后,恢复原数组状态,即回溯...
全排列是计算机科学中一种常见的算法问题,主要涉及组合数学和递归思想。在编程中,全排列通常指的是给定一个包含n个不同元素的序列,找出所有可能的n!种排列方式。这个问题广泛应用于数据处理、数据分析以及各种...
总结来说,这个压缩包提供的SQL数据库是一个存储五个字母全排列的工具,可能用于各种用途,比如密码生成、字符集分析或是其他需要遍历所有可能组合的场景。理解并操作这个数据库需要基本的SQL知识,以及对全排列概念...
在Python中,可以使用内置的`itertools`模块来简化全排列问题的解决,`itertools.permutations()`函数可以直接生成一个可迭代对象,包含了输入序列的所有排列。 总结起来,"python练习fibonacci全排列"这个主题涵盖...
否则,它会遍历所有未处理的元素,将第一个未处理的元素与当前位置的元素交换,然后递归地处理剩余的元素。递归结束后,通过调用`swap`函数恢复原数组状态,实现回溯。 这个算法的时间复杂度是O(N!),因为它需要...
然后,它遍历未处理的部分,对每个字符进行交换,然后递归地处理剩余部分(l+1到r)。交换后的字符在下一次递归调用之前被回溯,以恢复原始字符串,这是回溯算法的关键步骤。 这段代码展示了如何使用递归和回溯算法...
但是,由于可选元素个数越来越少,我们的这个 N 位数不能象 10 进制数那样使用一个常数的 n-1次方来做基。因此,我们定义第 n 位的基为 (n-1)!,和 10 进制数将是类似的。 根据这个策略,我们可以得到每次选择的...
通过循环遍历数组,使用`swap`函数将数组元素随机交换,确保生成的序列中包含0。最后,输出生成的随机数序列。 **实验要求**可能包括理解随机全排列的概念,掌握两种生成方法的原理,能够编写出正确的C程序,并进行...