全排列算法

相关题目

牛客NC121字符串的排列

leetcode剑指 Offer 38. 字符串的排列

字典序算法

在这里插入图片描述

public class Solution{
    char[] charArray;
    ArrayList<String> res;
    
    /**
     * 使用字典序算法
     * @param str
     * @return
     */
    public ArrayList<String> Permutation(String str) {
        charArray = str.toCharArray();
        // 先按字典序排序
        Arrays.sort(charArray);
        res = new ArrayList<>();
        res.add(String.valueOf(charArray));

        while (nextString()){
            res.add(String.valueOf(charArray));
        }

        return res;
    }

    /**
     * 找到下一个排列
     * @return 是否成功找到下一个排列
     */
    private boolean nextString() {
        int len = charArray.length;

        int i = len - 2;
        // 从右往左,找到第一个比它右一位的数小的数据,x
        while (i >= 0 && charArray[i] >= charArray[i + 1])
            i--;

        // 当前字符串已是降序排列,证明所有排列已被找出
        if(i == -1)
            return false;

        int j = len - 1;
        // 从右往左,找到第一个比 x 大的数据
        while (j >= 0 && charArray[j] <= charArray[i])
            j--;

        // 使用 >= 和 <=可以过滤掉重复的排列

        // 交换
        swap(i, j);
        // 将i后面的数据按从小到大排序
        sort(i + 1);
        return true;
    }

    private void sort(int i) {
        // 快速排序
        quickSort(i, charArray.length - 1);
    }

    /**
     * 快速排序
     * @param low
     * @param high
     */
    private void quickSort(int low, int high) {
        if(low >= high)
            return;

        char temp = charArray[low];
        int l = low, r = high;
        while (l < r){
            while (l < r && charArray[r] > temp)
                r--;
            charArray[l] = charArray[r];

            while (l < r && charArray[l] <= temp)
                l++;
            charArray[r] = charArray[l];
        }
        charArray[l] = temp;

        quickSort(low, l - 1);
        quickSort(l + 1, high);
    }

    /**
     * 交换charArray[i]和charArray[j]
     * @param i
     * @param j
     */
    public void swap(int i, int j){
        char temp = charArray[i];
        charArray[i] = charArray[j];
        charArray[j] = temp;
    }
}

回溯法(深度遍历 + 剪枝)

结果不要求排序

如果结果不要求排序,则可以使用原地交换的方法

class Solution {
    // 返回结果字符串的List集合
    private List<String> res = new LinkedList<>();
    // 字符串s的字符数组sArray
    private char[] sArray;

    public String[] permutation(String s) {
        sArray = s.toCharArray();
        dfs(0);
        return res.toArray(new String[0]);
    }

    /**
     * 深度遍历 + 剪枝
     * @param x 递归的层级,也是字符串当前固定的位置
     */
    public void dfs(int x){
        // 已经到了字符串的最后一个位置
        if (x == sArray.length - 1) {
            res.add(new String(sArray));
            return;
        }

        // 在x位置上的字母的Set集合,不允许重复
        Set<Character> cSet = new HashSet<>();

        for (int i = x; i < sArray.length; i++){
            // 如果出现重复字母,及时剪枝
            if(cSet.contains(sArray[i]))
                continue;

            cSet.add(sArray[i]);
            // 通过交换模拟排列
            swap(i, x);
            dfs(x + 1);
            // 恢复sArray
            swap(i, x);
        }
    }


    /**
     * 交换sArray[i]和sArray[j]
     * @param i
     * @param j
     */
    public void swap(int i, int j){
        char temp = sArray[i];
        sArray[i] = sArray[j];
        sArray[j] = temp;
    }
}

结果要求排序

如果结果要求排序,原地交换会影响原本的排序状态,这时我们使用状态变量存储数据是否已在当前状态里。

class Solution {
    char[] charArray;
    ArrayList<String> res;
    StringBuilder sb;
    HashSet<Integer> visited;   // 已经访问过的字符的下标

    /**
     * 使用深度遍历 + 剪枝,回溯算法
     * @param str
     * @return
     */
    public ArrayList<String> Permutation(String str) {
        charArray = str.toCharArray();
        Arrays.sort(charArray);
        res = new ArrayList<>();
        visited = new HashSet<>();
        sb = new StringBuilder();
        dfs();

        return res;
    }
    
    /**
     * 交换charArray[i]和charArray[j]
     * @param i
     * @param j
     */
    public void swap(int i, int j){
        char temp = charArray[i];
        charArray[i] = charArray[j];
        charArray[j] = temp;
    }

    /**
     * 深度遍历 + 剪枝
     */
    private void dfs() {
        int len = charArray.length;

        if(sb.length() == len) {
            res.add(sb.toString());
            return;
        }

        // 当前位置已使用过的字母的集合
        HashSet<Character> set = new HashSet<>();
        for (int i = 0; i < len; i++){
            if(set.contains(charArray[i]) || visited.contains(i))
                continue;
            visited.add(i);
            sb.append(charArray[i]);
            set.add(charArray[i]);
            dfs();
			// 剪枝
            visited.remove(i);
            sb.deleteCharAt(sb.length() - 1);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值