剑指 Offer 38. 字符串的排列

思路: 递归回溯。

  将字符串转为字符数组后,DFS搜索所有排序方案,即一次固定一个字母。

  搜索时,通过swap操作来依序固定字母,得到一个排列方案,搜索后通过swap操作复原。

代码:

class Solution {
    Set<String> list = new HashSet<>();
    char[] arr;
    public String[] permutation(String s) { 
        arr = s.toCharArray();
        dfs(0);
        return list.toArray(new String[0]);        
    }
    void dfs(int index){
        if (index == arr.length-1){
            list.add(String.valueOf(arr));
            return;
        }
        for (int i = index; i < arr.length; i++){
            swap(index, i, arr);//将 i 固定在第 index 位,
            dfs(index+1);//固定第 index+1 位
            swap(index, i, arr);//恢复 i 的位置
        }
    }
    void swap(int i, int j, char[] c){
        char tmp = c[i];
        c[i] = c[j];
        c[j] = tmp;
    }
}

  考虑到字符串中可能存在重复的字母,重复字母间交换顺序不会增加新的排序方案,因此可以加入剪枝操作。

  使用HashSet记录已排序字母,如果下一个字母已在set中,则跳过。

修改后:

class Solution {
    Set<String> list = new HashSet<>();
    char[] arr;
    public String[] permutation(String s) { 
        arr = s.toCharArray();
        dfs(0);
        return list.toArray(new String[0]);        
    }
    void dfs(int index){
        if (index == arr.length-1){
            list.add(String.valueOf(arr));
            return;
        }
        HashSet<Character> set = new HashSet<>();//新建HashSet记录已固定字母
        for (int i = index; i < arr.length; i++){
            if (set.contains(arr[i]))//剪枝
                continue;
            set.add(arr[i]);//将即将固定的字母加入set
            swap(index, i, arr);//将 i 固定在第 index 位,
            dfs(index+1);//固定第 index+1 位
            swap(index, i, arr);//恢复 i 的位置
        }
    }
    void swap(int i, int j, char[] c){
        char tmp = c[i];
        c[i] = c[j];
        c[j] = tmp;
    }
}
posted @ 2021-02-24 23:08  zjcfrancis  阅读(52)  评论(0编辑  收藏  举报