全排列(交换,递归,有详细解释)


 
这段话是网上抄来:
1、首先看最后两个数4, 5。 它们的全排列为4 5和5 4, 即以4开头的5的全排列和以5开头的4的全排列。
由于一个数的全排列就是其本身,从而得到以上结果。
2、再看后三个数3, 4, 5。它们的全排列为3 4 5、3 5 4、 4 3 5、 4 5 3、 5 3 4、 5 4 3 六组数。
即以3开头的和4,5的全排列的组合、以4开头的和3,5的全排列的组合和以5开头的和3,4的全排列的组合.
从而可以推断,设一组数p = {r1, r2, r3, ... ,rn}, 全排列为perm(p),pn = p - {rn}。
因此perm(p) = r1perm(p1), r2perm(p2), r3perm(p3), ... , rnperm(pn)。当n = 1时perm(p} = r1。
为了更容易理解,将整组数中的所有的数分别与第一个数交换,这样就总是在处理后n-1个数的全排列

举2个例子来说明


123的全排列
--首先遍历元素,然后把遍历到的每一个元素都和第一个元素交换
第一个和第一个交换 就是1和1交换 最后还是123
那么就形成了 123  213  321 这样的3组
(交换后 再换回来 还原成123 因为后面的交换都是在123的基础上交换的 所以swap要写2次)

--检查每一组除了第一个之外的剩余元素, 如果这些元素个数是2,
那么就对这剩下的2个元素全排列 就是123 132 ,  213 231 , 321 312
2个元素的全排列很简单 就是把这2个元素交换位置就OK)

1234的全排列
--首先遍历元素,然后把遍历到的每一个元素都和第一个元素交换
那么就形成了 1234  2134  3214 4231 这样的4组

--检查每一组除了第一个之外的剩余元素, 如1234剩余的是234,发现是3个元素
那么问题就转换为求234的全排列了
接下来也是一样 问题转换为求134, 214, 231的全排列

像这样 总是对除了第一个之外的元素全排列, 每次元素的个数都在减少一个,
求N个元素全排列最终就变成求2的元素的全排列了



这里是思维过程 最后才是成品=====================================

package arrange;

public class MyArrange2 {
    char arr[];

    public MyArrange2(char[] arr) {
        this.arr = arr;
    }

    void function(char[] arr) {
        if(arr.length==2){
            System.out.println(arr[0]+""+arr[1]);
            System.out.println(arr[1]+""+arr[0]);
        }else{          
            for (int i = 0; i < arr.length; i++) {
                swap(0, i);
                function(getRemain(arr)); //getRemain的作用是取出除了第一个之外的剩余元素
                swap(0, i);
            }
        }
    }

    char[] getRemain(char[] arr) {
        char[] c = new char[arr.length - 1];
        for (int i = 0; i < c.length; i++) {
            c[i] = arr[i + 1];
        }
        return c;
    }

    private void swap(int i, int j) {
        char c;
        c = arr[i];
        arr[i] = arr[j];
        arr[j] = c;
    }


    public static void main(String[] args) {
        char[] arr = new char[] { '1', '2', '3' };
        MyArrange2 a = new MyArrange2(arr);
        a.function(arr);
    }
}

这样的程序运行起来后,发现一个问题
那就是输出的总是把问题最小化的结果, 前面已经确定下来的排列都弄丢了

因此下面就有改进的方案
上面是每次递归时 都是新的数组,  每次数组中的元素都在减少
下面是每次递归时 仍是原来的数组,只是每次只处理部分元素,所要处理的元素个数越来越少


==========================================

package arrange;

public class MyArrange3 {
    char arr[];

    public MyArrange3(char[] arr) {
        this.arr = arr;
    }

    void function(char[] arr,int k) {
        if(k==(arr.length-2)){
            printHead(arr);
            System.out.println(arr[arr.length-2]+""+arr[arr.length-1]);
            printHead(arr);
            System.out.println(arr[arr.length-1]+""+arr[arr.length-2]);
        }else{          
            for (int i = k; i < arr.length; i++) {
                swap(k, i); //这里就不能写0了, 因为本次的第一个元素要从自己需要处理的元素开始算
                function(arr,k+1);
                swap(k, i);
            }
        }
    }


    private void swap(int i, int j) {
        char c;
        c = arr[i];
        arr[i] = arr[j];
        arr[j] = c;
    }
  
    void printHead(char[] arr){
        for (int i = 0; i < arr.length-2; i++) {
            System.out.print(arr[i]);
        }
    }


    public static void main(String[] args) {
        char[] arr = new char[] { '1', '2', '3' };
        MyArrange3 a = new MyArrange3(arr);
        a.function(arr,0);
    }
}

//=========================================================================
C语言版

#include "stdio.h "
void perm(char arr[],int k,int len);
void swap(char *p,char *q);

char arr[]="1234";
void main(){
    perm(arr,0,4);

}

void perm(char arr[], int k,int len){
    if (k==len-2)
    {
        for (int j=0;j<len;j++)
        {
            printf("%c",arr[j] );  
        }
        printf("\n" );
        for (j=0;j<len-2;j++)
        {
            printf("%c",arr[j] );  
        }
        printf("%c",arr[len-1] );
        printf("%c",arr[len-2] );
        printf("\n" );
    }
    else
    {
        for (int i=k;i<len;i++)
        {
            swap(&arr[k], &arr[i] );
            perm(arr, k+1,len );
            swap(&arr[k], &arr[i] );
        }
    }

  
}

void swap(char *p,char *q){
    char c;
    c=*p;
    *p=*q;
    *q=c;
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值