首页 > 试题广场 >

全排列

[编程题]全排列
  • 热度指数:23732 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
给定一个由不同的小写字母组成的字符串,输出这个字符串的所有全排列。 我们假设对于小写字母有'a' < 'b' < ... < 'y' < 'z',而且给定的字符串中的字母已经按照从小到大的顺序排列。

输入描述:
输入只有一行,是一个由不同的小写字母组成的字符串,已知字符串的长度在1到6之间。


输出描述:
输出这个字符串的所有排列方式,每行一个排列。要求字母序比较小的排列在前面。字母序如下定义:
已知S = s1s2...sk , T = t1t2...tk,则S < T 等价于,存在p (1 <= p <= k),使得
s1 = t1, s2 = t2, ..., sp - 1 = tp - 1, sp < tp成立。

每组样例输出结束后要再输出一个回车。
示例1

输入

abc

输出

abc
acb
bac
bca
cab
cba
关键递归函数
// 可以将其想像成竖着排列的组
// 例如
// a a a
// b b b
// c c c
// 第一组先取 a,标记数组标记为 true,代表已访问过
// 然后递归调用
// 第二组由于 a 标记为 true 跳过 取 b
// 以此类推
// 第三组结束后输出
// 此时再依次将每一组用过的还原为 true
// 访问到第几组由调用栈决定,每一组访问到第几个由 for 循环 i 决定,一次排列中是否访问过由 tag 数组决定
private static void get(char[] s, int index, boolean[] tag, char[] out) {
    // 访问结束
    if (index == s.length) {
        System.out.println(out);
        return;
    }
    for (int i = 0; i < s.length; i++) {
        // 没有访问过
        if (!tag[i]) {
            out[index] = s[i];
            // 第一组内已访问过
            tag[i] = true;
            get(s, index + 1, tag, out);
            // 将访问过的还原
            tag[i] = false;
        }
    }
}


发表于 2021-03-01 00:34:26 回复(0)
Java ,使用回溯
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;

public class Main {
    static  boolean[] visited;
    static char[] array;
    static  ArrayList<String> list = new ArrayList<>();
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        array = scanner.next().toCharArray();
        visited = new boolean[array.length];
        gen(0,"");
        Collections.sort(list);
        for (String s1 : list) System.out.println(s1);
        //注意题目要求:每组样例输出结束后要再输出一个回车。
        System.out.println();
    }
    
    static void gen(int step,String cur){
        // 递归终止
        if (step==array.length){
            list.add(cur);
            return;
        }
        for (int i = 0; i <array.length ; i++) {
            if (!visited[i]){
                visited[i]=true;
                gen(step+1,cur+array[i]);
                // 回溯,清除现场
                visited[i]=false;
            }
        }

    }
}


发表于 2020-03-19 09:40:23 回复(0)
permutaion的dfs版本,当作模板记忆。

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner reader = new Scanner(System.in);
        char[] input = reader.next().toCharArray();
        Arrays.sort(input);
        ArrayList<Character> path = new ArrayList<>();
        boolean[] visited = new boolean[input.length];
        helper(input, path, visited);
        System.out.println();
    }
    public static void helper(char[] str, ArrayList<Character> path, boolean[] visited) {
        if (path.size() == str.length) {
            for (char c: path) {
                System.out.print(c);
            }
            System.out.println();
            return;
        }
        for (int i = 0; i < str.length; ++i) {
            if (visited[i])
                continue;
            path.add(str[i]);
            visited[i] = true;
            helper(str, path, visited);
            path.remove(path.size()-1);
            visited[i] = false;
        }
    }
}

发表于 2018-04-24 17:57:30 回复(0)
//用dfs写的,主要是排序和去重吧
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
public class Main{

    public static void main(String[] args){
        testFullArrMent();
    }

    static boolean[] flag;
    static List<String> list = new ArrayList<String>();
    public static void testFullArrMent(){
        Scanner sc = new Scanner(System.in);
        String s = sc.nextLine();
        char[] ch = s.toCharArray();
        char[] c = new char[ch.length];
        flag  = new boolean[ch.length];
        dfs(ch,c,0);
        Collections.sort(list);
        for(String str:list){
            System.out.println(str);
        }
        System.out.println();
    }


    private static void dfs(char[] ch, char[] c, int k) {
        // TODO Auto-generated method stub
        if(k==ch.length){
            String s = String.valueOf(c);
            if(!list.contains(s)){
                list.add(s);
            }

        }
        for(int i=0;i<ch.length;i++){
            if(!flag[i]){
                flag[i]=true;
                c[k]=ch[i];
                dfs(ch,c,k+1);
                flag[i]=false;
            }
        }
    }

}

编辑于 2018-04-06 23:26:54 回复(0)
/*
 * 思路 深搜字典排序
 * 尽可能的搜索每一条路径,一旦搜索到某条路径的尽头时,则回溯到该路径的上
 * 一节点,若上一节点有未搜过的边,则沿着这条边继续搜索,如此反复知道所有
 * 节点都被搜索过
 */
import java.util.*;

public class Main {
    static int[] used = new int[7];//标记某个字符是否被使用过
    static char[] a = new char[7];
    static int total;
    static int N;

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner input = new Scanner(System.in);

        String str = input.nextLine();
        String[] tokens = str.split(" ");

        for (int i = 0; i < tokens.length; i++) {
            char[] arr = tokens[i].toCharArray();
            Arrays.sort(arr);
            N = arr.length;
            dfs(0, arr);
               System.out.println();//好坑,这行不加,case通过率为0,想不明白,命名print()函数在每
//组测试案例完成之后都会输出换行,为什么还要增加
        }
    }

    static void dfs(int i, char[] arr) {
        if (i >= N) {//递归结束,打印结果
            print();
        }
        else
            for (int k = 0; k < N; k++) {
                if (used[k] == 0) {
                    used[k] = 1;//已使用过的标记
                    a[i] = arr[k];//赋值
                    dfs(i + 1, arr);
                    used[k] = 0;//还原为未使用
                }
            }
    }

    static void print() {
        for (int k = 0; k < N; k++) {
            System.out.print(a[k]);
        }
        System.out.println();
    }
}

发表于 2018-02-08 19:36:55 回复(0)
import java.util.Arrays;
import java.util.Scanner;
/*
 * 思路:
 * 当我试着分解问题时,发现,n个数(a1,...,an)的全排列可以分解为a1拼接(n-1)个数全排列.直到1个数的全排列.此时能够确定某一个排列.
 * 自然会去尝试一下递归处理
 * # 那么思考递归变量是哪些呢?
 * 1. 参与全排列的元素在变,用char[]数组变量表示
 * 2. 因为要打印完整的全排列,每次父问题分为前后两半之后,前半边字符串需要传递进来和后半边字符串进行拼接,然后打印.
 * 
 * # 递归的边界条件是什么呢?
 * 1. 我们能直观感受到只有一个数时,问题不能再分解,并且可以拼接,打印字符串了.
 * 2. 但是,我们可能要用递归变量来具体化边界情况. recursive(前半边字符串=不怎么好描述...,char[] = 1个元素(这个1很好确定),);
 * 
 * # 打印的顺序满足字典序吗?
 * 卧槽,是满足的.
 */
public class Main_Sure {
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		while (scanner.hasNextLine()) {
			char[] input = scanner.nextLine().toCharArray();
			Arrays.sort(input);//有坑,存在"dbc"这种顺序打乱的test case,所以排个序.
			fullPermutation("", input);
			System.out.println();
		}
	}

	public static void fullPermutation(String pre, char[] inputs) {
		if (inputs.length == 1) {
			System.out.println(pre.toString() + inputs[0]);
			return;
		}
		//分类计数原理计算全排列的排列个数: 每个字符都要做一次开头. 这样分类完备,无重复.
		//inputs[i]:某全排列开头的字符.
		for (int i = 0; i < inputs.length; i++) {
			char[] rest = new char[inputs.length - 1];
			int index = 0;
			for (int j = 0; j < inputs.length; j++) {
				if (i != j) {
					rest[index++] = inputs[j];
				}
			}
			fullPermutation(pre +""+inputs[i], rest);
		}
	}
}
编辑于 2017-04-01 12:06:11 回复(0)