求全排列的两种方法(Java)

2023-10-28 02:30
文章标签 java 方法 两种 排列 求全

本文主要是介绍求全排列的两种方法(Java),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

递归法

假设我们有0,1,2,3四个数需要全排列,递归法是一种比较类似于深度搜索的方法,直到递归最深处,才得出结果。

其大概思路是,设置一个游标start,游标所到之处,其左侧已经考虑过,利用递归思想求剩下的全排列。

仔细分析,依次考虑每一位数。由于对于每一位数,它可能的数包括除了之前考虑过的所有数,因此将start右侧(包括start)的所有数都和start这一位交换一次,每交换后,马上进入递归函数(start+1),接着再执行一次原交换,用于复原。这样就做成了把每一个数都在这个位上排列一次,递归函数的调用表示对剩下的数进行全排列。

代码如下:

package main;public class Main {static int[] a = {0, 1, 2, 3};static int result = 0;public static void main(String[] args) {dfs(0);System.out.println("The total of all permutation of a[] is: " + result);}public static void dfs(int start) {if (start >= a.length) {System.out.print(a[0]);for (int i = 1; i < a.length; i++) {System.out.print(", " + a[i]);}System.out.println();result++;return;}for (int i = start; i < a.length; i++) {swap(start, i);dfs(start + 1);swap(start, i);}}public static void swap(int i, int j) {int temp = a[i];a[i] = a[j];a[j] = temp;}
}

给出输出结果:
在这里插入图片描述

回溯法

这种方法叫回溯法,他的思想也是用到递归的思想,长得也和上面递归法很像,只是从递归函数的解读来说,和递归法还是有区别的。(leetcode上经常有回溯法求解的题)

在回溯法中,通常有多个辅助变量:待选列表或集合,已选列表或集合,表示结果的列表或集合,等等。其递归思想,就是

  1. 每次遍历待选
  2. 更新表示结果的列表或集合
  3. 更新已选列表或集合
  4. 执行递归操作
  5. 恢复表示结果的列表或集合,恢复已选列表或集合。

这么看来,好像是和递归法没什么区别。我的理解是,递归法和回溯法本质上都是递归,在求全排列问题上,递归解法和回溯法极其相似,但是在一些应用问题中,用递归法不是那么好理解,而回溯法的思考模式和我们人想问题差不多,递归套路就是挨个试,因此回溯法还是比较容易接受的。

还有一点要注意的是,在全排列问题上,回溯法用的复杂度较高,效率低于递归法。

下面附上代码:

package main;import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;public class Main {static int[] a = {0, 1, 2, 3};static Set<Integer> all = new HashSet<Integer>(); // 全体集合(待选集合=全体集合-已选集合)static Set<Integer> used = new HashSet<Integer>(); // 已选集合static List<List<Integer>> resultList = new ArrayList<>(); // 全局结果列表public static void main(String[] args) {for (int i = 0; i < a.length; i++) {all.add(a[i]);}backtrack(0, new ArrayList<Integer>());System.out.println("The total of all permutation of a[] is: " + resultList.size());}public static void backtrack(int p, List<Integer> tempresult) {if (p >= a.length) {System.out.print(tempresult.get(0));for (int i = 1; i < tempresult.size(); i++) {System.out.print(", " + tempresult.get(i));}System.out.println();resultList.add(tempresult);return;}for (Integer n : all) {if (!used.contains(n)) {used.add(n);tempresult.add(n);backtrack(p + 1, tempresult);tempresult.remove(tempresult.size() - 1);used.remove(n);}}}public static void swap(int i, int j) {int temp = a[i];a[i] = a[j];a[j] = temp;}
}

执行结果:
在这里插入图片描述

执行结果对比

比较上面两个方法中,输出执行结果的区别,发现:

在递归法中:
在这里插入图片描述
回溯法中:
在这里插入图片描述
由于两种方法(尽管都用到了递归)对递归含义的解释,也就是执行思路不同,导致了结果输出顺序的不同。

在递归法中,先输出0321,后输出0312,是因为此时正在考虑第二位(start=1),已经对这一位考虑过了数字1和数字2,现在需要将数字3和原本的1做交换,而这个操作和数字2无关,所以数字2还在它原来的位置上。直到考虑第三位时(start=2),才与后面的1交换,输出了0321.

在回溯法中,先输出0312,后输出0321,这是因为,在集合的遍历中,由于对集合all的遍历顺序实际上是字典序(数字从小到大),因此这就好比我们人的思维模式,逐位考虑可能的数字,而考虑的顺序就是字典序了。这样最后输出的全排列顺序,也是字典序。

谢谢!

这篇关于求全排列的两种方法(Java)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/290070

相关文章

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java使用ANTLR4对Lua脚本语法校验详解

《Java使用ANTLR4对Lua脚本语法校验详解》ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,下面就跟随小编一起看看Java如何使用ANTLR4对Lua脚本... 目录什么是ANTLR?第一个例子ANTLR4 的工作流程Lua脚本语法校验准备一个Lua Gramm

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

Java Optional的使用技巧与最佳实践

《JavaOptional的使用技巧与最佳实践》在Java中,Optional是用于优雅处理null的容器类,其核心目标是显式提醒开发者处理空值场景,避免NullPointerExce... 目录一、Optional 的核心用途二、使用技巧与最佳实践三、常见误区与反模式四、替代方案与扩展五、总结在 Java

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

Java字符串处理全解析(String、StringBuilder与StringBuffer)

《Java字符串处理全解析(String、StringBuilder与StringBuffer)》:本文主要介绍Java字符串处理全解析(String、StringBuilder与StringBu... 目录Java字符串处理全解析:String、StringBuilder与StringBuffer一、St

springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法

《springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法》:本文主要介绍springboot整合阿里云百炼DeepSeek实现sse流式打印,本文给大家介绍的非常详细,对大... 目录1.开通阿里云百炼,获取到key2.新建SpringBoot项目3.工具类4.启动类5.测试类6.测

Python列表去重的4种核心方法与实战指南详解

《Python列表去重的4种核心方法与实战指南详解》在Python开发中,处理列表数据时经常需要去除重复元素,本文将详细介绍4种最实用的列表去重方法,有需要的小伙伴可以根据自己的需要进行选择... 目录方法1:集合(set)去重法(最快速)方法2:顺序遍历法(保持顺序)方法3:副本删除法(原地修改)方法4:

Spring Boot循环依赖原理、解决方案与最佳实践(全解析)

《SpringBoot循环依赖原理、解决方案与最佳实践(全解析)》循环依赖指两个或多个Bean相互直接或间接引用,形成闭环依赖关系,:本文主要介绍SpringBoot循环依赖原理、解决方案与最... 目录一、循环依赖的本质与危害1.1 什么是循环依赖?1.2 核心危害二、Spring的三级缓存机制2.1 三