回溯法-求全排列

–所谓有路则通,无路则返–

理解一

比如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

  1. public class Main {  
  2.     static int count = 0;  
  3.     public static void main(String[] args) {  
  4.         int[] a = {1,2,3};  
  5.         test(a,0);  
  6.         System.out.println(count);  
  7.     }  
  8.     public static void test(int[] a,int b){  
  9.         if(b>=a.length){  
  10.             count++;  
  11.             System.out.println(java.util.Arrays.toString(a));  
  12.         }  
  13.         for (int i = b; i < a.length; i++) {  
  14.             {int k = a[b];a[b] = a[i];a[i] = k;}//两个数字,进行交换  
  15.             test(a,b+1);//用递归进行下一个数的交换  
  16.             {int k = a[b];a[b] = a[i];a[i] = k;}//再次交换两个数字,换回来  
  17.         }  
  18.     }  
  19. }  

输出结果:

[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

  1. public class Main {  
  2.     static int count = 0;  
  3.     public static void main(String[] args) {  
  4.         int[] a = {1,2,2};  
  5.         test(a,0);  
  6.         System.out.println(count);  
  7.     }  
  8.     public static void test(int[] a,int b){  
  9.         if(b>=a.length){  
  10.             count++;  
  11.             System.out.println(java.util.Arrays.toString(a));  
  12.         }  
  13.         for (int i = b; i < a.length; i++) {  
  14.             if(!is(a,b,i)) continue;   
  15.             {int k = a[b];  
  16.             a[b] = a[i];  
  17.             a[i] = k;}  
  18.             test(a,b+1);  
  19.             {int k = a[b];  
  20.             a[b] = a[i];  
  21.             a[i] = k;}  
  22.         }  
  23.     }  
  24.     /*排除重复情况的组合*/  
  25.     public static boolean is(int[] a,int b,int i){  
  26.         for (int j = b; j < i; j++) {  
  27.             if(a[i]==a[j]){  
  28.                 return false;  
  29.             }  
  30.         }  
  31.         return true;  
  32.     }  
  33. }  

输出结果:

[1, 2, 2] [2, 1, 2] [2, 2, 1] 3

借鉴一篇关于回溯法求全排列的博客来理解:

http://blog.csdn.net/Iloveyougirls/article/details/55044401
自己理解还是不透彻,还得多练几道此类型的题目-_-!

 蓝桥杯,考暴力和搜索,这是众所周知的事情,近几年的题目非常非常的多。

        搜索的基本理论:

                   1、回溯法:当把问题分成若干个步骤并递归求解时,如果当前步骤没有合法选择,则函数将返回上一级递归调用,这种现象就称回溯。

                   2、路径寻找问题:路径寻找问题可以归结为隐式图的遍历,它的任务是找到一条从初始状态到终止状态的最优路径,而不是像回溯法那样找到一个符合某些要求的解。

                      常见的两种方法是:深度优先搜索,广度优先搜索。

    原文作者:回溯法
    原文地址: https://blog.csdn.net/sinat_37341950/article/details/79177371
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞