全排列
1. 交换法:
将要全排列的各个元素放在数组中,然后数组中的元素进行交换
- 先考虑第一位的情况,依次 考虑剩余位
例如 ; [ A, B ,C]
数组下标;k=0 (第一位) : A[] B[] C[]
k=1(第二位):在 A[]这个分支: AB[C] AC[B]
k=2(第三位):在 A[]这个分支: ABC ACB 自己和自己交换
在另外两个分支上是相同的过程;
但在走第二个分支之前,一定要恢复到未排序时的顺序,称为还原,因为在递归过程中,每次递归都使用同一个引用变量数组 - 怎么表示多分支递归? ** 在循环中递归**
使用for循环: for(i=k....n-1) swap(k,i); 得到多个分支(就是各个都占据一遍第一位) - 在此多分支下,再f(arr,k+1) 处理第一位后面的剩余位
- 最后,还原,再次swap(k,i)
在for循环中i一定下来,然后执行递归f(arr,k+1)
比如:在 A[]这个分支
k=0 A[BC] k=1 AB[C] k=2 ABC
回溯到:k=1 for循环 执行 AC[B] k=2 ACB
回溯到:k=0 A[BC] 还原为 ABC
最外层for, 执行 B[]这个分支:
k=0 B[AC] k=1 BA[C] k=2 BAC
回溯到:k=1 for循环 执行 BC[A] k=2 BCA
回溯到:k=0 B[AC] 还原为 ABC
最外层for, 执行 C[]这个分支
因为多个分支开始于同一个起点,所以每排完一个分支,就还原为初始态
整个执行过程是先纵后横
package 递归;
import java.util.ArrayList;
import java.util.Arrays;
public class PaiLie {
static ArrayList<String> list = new ArrayList<>();//静态的全局变量
public static void main(String[] args) {
list = new PaiLie().getArrayList("cbad");
System.out.println(list);
}
public ArrayList<String> getArrayList(String a){
char[] arr = a.toCharArray();
Arrays.sort(arr);//abcd
sort1(arr, 0);
return list;
}
public static void sort1(char[] arr,int k) {
//递归下去,k在不断的增加
if(k==arr.length) {//说明排好了一种情况,就是递归的一个分支走到底,就将排好的一种添加到list集合中
list.add(new String(arr));
}
//从k位开始的每个字符,都尝试放在新排列的k这个位置
for(int i=k;i<arr.length;i++) {
swap(arr,k,i);//把后面每个字符换到k位
sort1(arr, k+1);//将k+1位和其后的都排好
swap(arr,k,i);//还原 回溯
}
}
//交换数组元素
public static void swap(char[] arr,int i,int j) {
char temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
//[abcd, abdc, acbd, acdb, adcb, adbc, bacd, badc, bcad, bcda, bdca, bdac, cbad, cbda, cabd, cadb, cdab, cdba, dbca, dbac, dcba, dcab, dacb, dabc]
}
交换法无法维持字典序
2.下面介绍前缀法:
排列是在前缀上逐步增加的
顺序扫描字符集(在其中各个字符都按照字典序排列好的),若不在我们的集合中,就将此字符放进去
每次都从头到尾扫描,符合条件就将字符放到集合中,已放进的字符作前缀
比如:abc
形成三个分支:
[a ] [b ] [c ]
在[a ]分支,递归下去,[a b ] 再是 [a b c] 回溯到 [a c ] 再是 [a c b]
接下来 [b ] 递归下去, [b a] 再是 [ b a c] 回溯到 [ b c ] 再是 [b c a]
接下来 [c ] 递归下去, [c a] 再是 [ c a b] 回溯到 [ c b ] 再是 [ c b a]
输出:
abc acb bac bca cab cba 依次是字典序,对每一个字符串就是一个一个字符的比较,就第一个不同的字符在字典序中的位置进行比较
package 递归;
//n个数的排列组合中找出字典序的第k个排列
public class PaiLie2 {
public static void main(String[] args) {
String s = "abc";
sort2("", s.toCharArray());
}
final static int k = 3;
static int count = 0;//统计字典序的组合数
/**
* @param string 前缀
* @param arr 字符集
*/
public static void sort2(String string ,char[] arr) {
//出口
//前缀的长度等于字符集的长度,一个排列就完成了
if(string.length() == arr.length) {
count++;
if(count == k) {
System.out.println(string);
System.exit(0);
}
}
//每次都从头扫描,只要符合条件就将字符放到集合中,已放进的字符作前缀
for(int i=0;i<arr.length;i++) {
char ch = arr[i];
//字符符合条件是:此字符在前缀中出现的次数要小于此字符在字符集出现的次数
if(count(string.toCharArray(),ch)<count(arr,ch)) {
sort2(string+ch,arr);//string+ch 符合条件的字符拼接到前缀中
}
}
}
//统计字符出现次数
public static int count(char[] arr,char ch) {
int cnt = 0;
for(char c:arr) {
if(c == ch) {
cnt++;
}
}
return cnt;
}
}
//输出:bac
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现