全排列是一种比较烦人的东西。本文讨论如何在不使用递归的情况下,让全排列结果能够被遍历(也就是实现 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},不信把整个树画出来自己对对看。
实际上,因为叶子节点的位置都是绝对的,我就不一定非要从第一个结果来排起。给出任意一个排列结果,我都可以找到它在这棵树中的位置,然后直接求得下一个位置和下一个位置的排列结果。
基本的思路就是这些,然后就是用代码来实现。至于怎么思考实现的就略去了,下面是代码:
使用方法:
代码仅用于检验思路,写法不甚严格。若要实用,最好能对参数加一些判断。
本文发表于: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 代码
- import sun.reflect.generics.reflectiveObjects.NotImplementedException;
- import java.util.Iterator;
- /**
- * 全排列树 -- 可遍历的全排列
- */
- class SortTree implements Iterator {
- private int level;
- private int[] defaultArr; // {1, 2, 3, 4, 5, ...} 这样一个数组
- private int[] currentPosition;
- public SortTree(int level) {
- this.level = level;
- init();
- }
- private void init() {
- defaultArr = new int[level];
- for (int i = 0; i < level; i++) {
- defaultArr[i] = i;
- }
- currentPosition = getBeforeStartPosition();
- }
- /**
- * 获得指定位置的全排列
- *
- * @param position 全排列在树中的位置
- *
- * @return 处于指定位置的全排列
- */
- public int[] getValue(int[] position) {
- int[] cloned = defaultArr.clone();
- if (position.length != level - 1) {
- System.out.println("invalid position level");
- return new int[0];
- }
- for (int i = 0; i < position.length; i++) {
- swap(cloned, i, i + position[i]);
- }
- return cloned;
- }
- /**
- * 获得指定的全排列的位置
- *
- * @param value 全排列中的一项
- *
- * @return 指定的项在全排列树中的位置
- */
- public int[] getPosition(int[] value) {
- int[] cloned = defaultArr.clone();
- int[] position = new int[value.length - 1];
- for (int i = 0; i < value.length - 1; i++) {
- int pointer = 0;
- if (value[i] == cloned[i]) {
- position[i] = 0;
- } else {
- pointer++;
- while (pointer < value.length) {
- if (value[i] == cloned[pointer]) {
- swap(cloned, i, pointer);
- position[i] = pointer - i;
- }
- pointer++;
- }
- }
- }
- return position;
- }
- /**
- * 获得下一个位置
- *
- * @param position 当前位置
- *
- * @return 下一个位置
- */
- public int[] getNextPosition(int[] position) {
- int[] result = position.clone();
- for (int i = result.length - 1; i >= 0; i--) {
- int upper = result.length - i;
- if (result[i] + 1 <= upper) {
- result[i] += 1;
- break;
- } else {
- result[i] = 0;
- }
- }
- return result;
- }
- /**
- * 是否还有下一个位置
- *
- * @param position 当前位置
- *
- * @return 如果还有则返回 true。
- */
- public boolean hasNextPosition(int[] position) {
- for (int i = 0; i < position.length; i++) {
- if (position[i] != defaultArr.length - i - 1) {
- return true;
- }
- }
- return false;
- }
- /**
- * 获得第一个位置之前的位置
- *
- * @return 第一个位置之前的位置
- */
- public int[] getBeforeStartPosition() {
- int[] position = new int[defaultArr.length - 1];
- position[position.length - 1] = -1;
- return position;
- }
- private void swap(int[] ints, int a, int b) {
- int t = ints[a];
- ints[a] = ints[b];
- ints[b] = t;
- }
- public boolean hasNext() {
- return hasNextPosition(currentPosition);
- }
- public Object next() {
- currentPosition = getNextPosition(currentPosition);
- return getValue(currentPosition);
- }
- public void remove() {
- throw new NotImplementedException();
- }
- }
使用方法:
java 代码
- SortTree tree = new SortTree(6);
- int counter = 0;
- while (tree.hasNext()) {
- int[] sort = (int[]) tree.next();
- printArray(sort);
- counter++;
- }
- System.out.println("一共 " + counter + " 个结果");
代码仅用于检验思路,写法不甚严格。若要实用,最好能对参数加一些判断。
本文介绍了一种不使用递归实现全排列遍历的方法,通过构建全排列树并利用迭代器接口提供下一排列结果,确保遍历过程不重复且有序。
832

被折叠的 条评论
为什么被折叠?



