• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
百事可爱
一起努力鸭~~~
博客园    首页    新随笔    联系   管理    订阅  订阅
全排列

全排列

1. 交换法:

将要全排列的各个元素放在数组中,然后数组中的元素进行交换

  1. 先考虑第一位的情况,依次 考虑剩余位
    例如 ; [ A, B ,C]
    数组下标;k=0 (第一位) : A[] B[] C[]
    k=1(第二位):在 A[]这个分支: AB[C] AC[B]
    k=2(第三位):在 A[]这个分支: ABC ACB 自己和自己交换
    在另外两个分支上是相同的过程;
    但在走第二个分支之前,一定要恢复到未排序时的顺序,称为还原,因为在递归过程中,每次递归都使用同一个引用变量数组
  2. 怎么表示多分支递归? ** 在循环中递归**
    使用for循环: for(i=k....n-1) swap(k,i); 得到多个分支(就是各个都占据一遍第一位)
  3. 在此多分支下,再f(arr,k+1) 处理第一位后面的剩余位
  4. 最后,还原,再次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
posted on 2022-03-11 19:50  精致猪猪侠  阅读(159)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3