–所谓有路则通,无路则返–
理解一
比如123,在第一个位置摆放的时候,有3种情况,用一个for循环,第一次找的是1,1就被标记了,再找第二位第三位的时候,1就不可以用了。
再找第二位,剩下2和3,由于1被标记了,只可以找2或者3,先取的是2,取后标记2。
再找第三位,剩下了3,由于12都被标记了,只可以找3,所以直接就是3。
第一个序列123就出来了。由于3只有一种情况,他一次就执行完了。
看到第二位,剩下2和3,2取过了,现在取3,所以最后一位只能取2了。
这样最后两位的变化都已经搞完了。
第一位又可以做文章了。1取过了,这里就取到了2。那相应的,第二位和第三位也就剩下了1和3,也是一样的操作原理。
最后第一位取3,思路同样。
private void sort(int level) { for (int i = 0; i < n; i ++) { if (!flag[i]) { current[level] = i + 1; flag[i] = true;//比如倒数第二层:锁一个 if (level != n - 1) { sort(level + 1);//留一个给最后一层 } else { System.out.println(Arrays.toString(current)); } flag[i] = false;//最后一层搞完后,恢复这一层,让剩下那个数再搞 } } }
其实也可以这样理解:
宏观来讲:这个方法的功能也就是从当前位开始到最后,找到所有可能排列的情况。
微观来讲:其实就是从for循环中找出未标记过的一个数,把他标记了,再把剩下未标记的数去递归,交给子方法去做。如果都选完了,那么输出就可以了。
还有一个问题,他是怎么回溯的?其实你for循环其实里面有一重又一重的for循环(因为你采用了递归),等你最里面那一层执行完了,自然会执行上一层的东西,所以他是会自动回溯的,你只需要考虑的是,回溯以后,本来被你标记的东西,重新变为未标记状态即可。
理解二
一、对于不重复数字的全排列(例如:1,2,3,4,5,7,8,9)
1.用数学的方法来算很简单,排列的结果是9!种情况;
下面是简单的一个代码的例子:
[java]
view plain
copy
- public class Main {
- static int count = 0;
- public static void main(String[] args) {
- int[] a = {1,2,3};
- test(a,0);
- System.out.println(count);
- }
- public static void test(int[] a,int b){
- if(b>=a.length){
- count++;
- System.out.println(java.util.Arrays.toString(a));
- }
- for (int i = b; i < a.length; i++) {
- {int k = a[b];a[b] = a[i];a[i] = k;}//两个数字,进行交换
- test(a,b+1);//用递归进行下一个数的交换
- {int k = a[b];a[b] = a[i];a[i] = k;}//再次交换两个数字,换回来
- }
- }
- }
输出结果:
[1, 2, 3] [1, 3, 2] [2, 1, 3] [2, 3, 1] [3, 2, 1] [3, 1, 2] 6
二、有些时候,有重复的数字进行排列组合(例如:1,2,2)
这样的数字比较少的,很简单一眼就能看得出有3种情况的排列;
把上面的代码稍做判断就可以的到重复数字的全排列:
[java]
view plain
copy
- public class Main {
- static int count = 0;
- public static void main(String[] args) {
- int[] a = {1,2,2};
- test(a,0);
- System.out.println(count);
- }
- public static void test(int[] a,int b){
- if(b>=a.length){
- count++;
- System.out.println(java.util.Arrays.toString(a));
- }
- for (int i = b; i < a.length; i++) {
- if(!is(a,b,i)) continue;
- {int k = a[b];
- a[b] = a[i];
- a[i] = k;}
- test(a,b+1);
- {int k = a[b];
- a[b] = a[i];
- a[i] = k;}
- }
- }
- /*排除重复情况的组合*/
- public static boolean is(int[] a,int b,int i){
- for (int j = b; j < i; j++) {
- if(a[i]==a[j]){
- return false;
- }
- }
- return true;
- }
- }
输出结果:
[1, 2, 2] [2, 1, 2] [2, 2, 1] 3
借鉴一篇关于回溯法求全排列的博客来理解:
http://blog.csdn.net/Iloveyougirls/article/details/55044401
自己理解还是不透彻,还得多练几道此类型的题目-_-!
蓝桥杯,考暴力和搜索,这是众所周知的事情,近几年的题目非常非常的多。
搜索的基本理论:
1、回溯法:当把问题分成若干个步骤并递归求解时,如果当前步骤没有合法选择,则函数将返回上一级递归调用,这种现象就称回溯。
2、路径寻找问题:路径寻找问题可以归结为隐式图的遍历,它的任务是找到一条从初始状态到终止状态的最优路径,而不是像回溯法那样找到一个符合某些要求的解。
常见的两种方法是:深度优先搜索,广度优先搜索。