全排列是一个比较经典的问题,昨天被问到全排列算法,竟一时语塞,因为的确我的算法积累太单薄,研究得太少. 多关注这些问题,对分析问题解决问题还是很有帮助的.
废话不多说,进入正题吧. 首先还是写个虚基类,即便用不到呵呵~虚基类里照旧是一些必要方法和虚的permutate()方法.这个代码就不贴了,可以参见笔者另一篇文章.
递归解决这个问题是比较常见的,实际上递归的思想也是很不错的,虽然会占用较多空间.考虑两个数的全排列就是<1 2>或者<2 1>,三个数的全排列就是取尽这个数组的任意一个数a(a属于{Vn})与全排列剩下两个数,构成的排列...N个数就是取尽每一个数,然后全排列剩下N-1个数,构成的排列.
设定一个可比较值大小的数组{V(n)}.perm({V(n)})=V1#perm({V(n-1)}/V1)+V2#perm({V(n-1)}/V2)+...+Vn#perm({V(n-1)}/Vn). 其中perm({V(n-1)}/Vn)表示除了元素Vn以外的剩下的数组.
根据以上算法,笔者尝试用自己浅薄的思维来实现这个基本算法,
首先是数据的存储和表示问题.由于一开始就不打算用Collection,而就着简单的数组,而笔者不喜欢走回头路,所以就盯着数组不放了.数组,就面临着没办法自由删除节点,因为数组都是连续固定长度的存储空间.如何表示节点的删除是一个问题.
第二是多分支递归问题.因为递归分支是多达n个(n随着递归深度的增加而减少,初始递归的分支是n个),每个分支都需要本次过程所需的各自不同的原材料,原材料因此也需要n份,如果把初始数组复制n份是不是太浪费空间了呢?如果不这样做,那么第三个问题,即如何输出排列呢?
第三是排列如何输出的问题.这个问题又是和第一二个问题耦合的.因为数组时固定的,而多个分支递归传入参数的信息需要各不相同并且互不影响(这保证输出的正确性),如果每次都任取一个元素(即便是按规律取元素)立即打印输出, 那么多个分支都在交错打印输出,在控制台上人眼就毫无办法区分,相当于还是没有成功排列.
既然用了数组,所占空间固定无法移除,而恐怕又因为问题2而导致的原料复制问题,可想而知N个数就要复制N!次...这个代价是很不情愿看到的,笔者自己暂时没有想到好的解决.而采用一个伴随的布尔数组的想法,用布尔数组来表示数组中元素取出情况 布尔值本身所占空间很小,复制N!次对内存的占用仍旧能控制在一个可预见的范围.(题外话::Java解释器对布尔数组应该也会有优化,即声明一个布尔值可能占用了一个byte,但是new一个布尔数组所占用空间未必就达到n个byte::以上需要验证)
现在考虑解决交错输出问题,这个问题笔者仍然没有找到好的解决办法,而采用了一个和布尔数组类似的先存后输出方法,即这样的方法,所需要的String的个数仍然将达到N!个,这个事实上没办法避免了,因为N个数的全排列有N!个,那么在这个解决思路下,就需要N!个String来存储并最后打印输出,为了避免直接的String操作(那样更费空间),采用的是StringBuilder类.无论如何这也是较小范围的优化了,需要一个更好的算法实现.
代码如下,欢迎拍砖
@Override
public void permutate() {
boolean[] fetchInfo = InitBooleans();
doPerm(arrayEs, fetchInfo, arrayEs.length, new StringBuilder(""));
}
/**
* core permutation recursion function
* @param aEs the array to permulate
* @param FetchedInfo the info of fetch or not
* @param restnum indicates how many elements remain untouched.
* @param trace the result tracing String builder
*/
private void doPerm(E[] aEs, boolean[] FetchedInfo, int restnum, StringBuilder trace) {
if (restnum == 1) {
lastFetchAndPrint(aEs, FetchedInfo, trace);
return;
} else {
// 得到restnum个复制的fetchInfo[],两两不同
for (int i = 1; i <= restnum; i++) {
// 新建一个布尔数组,表达下一步迭代的,并决定被输出值(被取出值)
StringBuilder inheritedTrace = new StringBuilder(trace.toString());
boolean[] newFetched = stepDownBooleans(aEs, FetchedInfo, i,
restnum, inheritedTrace);
doPerm(aEs, newFetched, restnum - 1, inheritedTrace);
}
trace = null;
}
}
private boolean[] stepDownBooleans(E[] aEs, final boolean[] oldFetched,
final int whichStep, final int restnum, StringBuilder trace) {
if (whichStep > restnum || whichStep <= 0) {
System.err.println("algorithm fails.");
throw new IllegalArgumentException();
}
boolean[] newFetched = new boolean[oldFetched.length];
int count = 0;
for (int i = 0; i < newFetched.length; i++) {
if (oldFetched[i]) {// 如果满足不是false并且轮到某个true节点该他置为假,那就置之
count++;
if (count == whichStep) {
newFetched[i] = false;// 取出(置为假值)
trace.append(aEs[i] + " ");// 输出
} else {
newFetched[i] = oldFetched[i];// true
}
} else {// 否则简单复制(已有假值)下来即可
newFetched[i] = oldFetched[i];// false
}
}
return newFetched;
}
分享到:
相关推荐
下面我们将深入探讨如何使用Java实现字符数组的全排列。 首先,我们需要了解回溯法。回溯法是一种试探性的解决问题方法,它尝试逐步找到问题的所有解。当发现某一步无法继续找到有效解时,会退回一步,尝试其他的...
JAVA递归实现全排列算法,含实现源代码,如a、b、c、d的全排列为: abcd abdc acbd acdb adcb adbc bacd badc bcad bcda bdca bdac cbad cbda cabd cadb cdab cdba dbca dbac dcba dcab dacb dabc
在这个场景中,我们将探讨如何使用Java语言,通过回溯法来递归实现全排列的输出。 首先,我们需要理解回溯法的基本概念。回溯法是一种试探性的解决问题的方法,它尝试逐步构建解决方案,并在每一步中检查当前的解...
在本例中,我们将讨论如何使用递归方法实现全排列,以Java、C#、C++等主流编程语言为例。 全排列算法的核心思想是通过递归地交换元素来生成所有可能的序列。假设我们有一个包含n个不同元素的数组,全排列的数量是n...
在Java中实现重复元素全排列,通常采用递归的方法。核心思想是通过交换元素的位置来生成不同的排列组合,并检查每次交换是否产生了一个新的、未被记录的排列。为了避免重复计算,可以使用一个辅助函数`Judge()`来...
Java 全排列算法实现,网上搜的,然后整理了一下。呵呵`````
java实现全排列
全排列的非递归实现。 输入1,2,3,4 得到 [1 2 3 4]..........[4 3 2 1]所有24种排列
在Java中,实现全排列通常会用到递归或者回溯法。Hash函数在这里的作用是将当前的排列状态转换为一个唯一的键(key),然后存储到哈希表中。这样,当生成新的排列时,可以通过Hash函数快速判断这个排列是否已经出现...
"java实现字符串的全排列" java实现字符串的全排列是指通过编程语言java来生成一个字符串的所有可能排列。例如输入字符串abc,则输出所有可能的排列组合:abc,acb,bac,bca,cab和cba。 在java中,实现字符串的...
java 递归,abcd全排列,非常简单的。
以下是一个Java实现n位数字全排列的示例代码: ```java public class Test { static int k = 0; public static void main(String[] args) { int a[] = {1, 2, 3, 4, 5}; // 定义一个n位数字数组 permutations...
利用换位法求小于9的所有全排列序列
题目描述:给定一个数列a1,a2,a3…an,输出他所有的全排列。 算法设计描述: 1、获取当前的一种排列,用start,end分别表示该排列的列头,列尾; 2、判断start是否和end相等,若相等,执行3,否则执行4; 3、将当前...
"JAVA用递归实现全排列算法的示例代码" JAVA用递归实现全排列算法的示例代码主要介绍了JAVA用递归实现全排列算法的相关资料。全排列算法是一种经典的算法,在数学和计算机科学领域中有着广泛的应用。该算法的主要...
在Java中,我们可以使用递归的方式来实现全排列。递归的基本思路是,对于n个元素的全排列问题,我们先选择一个元素作为排列的第一个,然后对剩下的n-1个元素进行全排列。这样,每次递归调用都会解决规模更小的问题,...
用序数法生成全排列,java语言,希望有帮助
在Java和C#这两个广泛使用的编程语言中,有许多不同的方法可以实现全排列。接下来,我们将深入探讨这两种语言中实现ABCD全排列的7种方法。 1. **回溯法**: 回溯法是一种典型的递归策略,适用于解决约束满足问题。...
利用中介数实现全排列算法,采用java实现。
总结,全排列算法主要通过递归或迭代实现,利用深度优先搜索或回溯策略。优化方法包括剪枝和记忆化,以减少重复计算和提高效率。对于具体的问题,需要根据实际需求和数据规模选择合适的实现方式。在编程实践中,理解...