LeetCode刷题笔记 ~ 剑指 Offer 38. 字符串的排列

509 阅读1分钟

这是我参与更文挑战的第8天,活动详情查看:更文挑战

剑指 Offer 38. 字符串的排列

输入一个字符串,打印出该字符串中字符的所有排列。

你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

示例

输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]

提示

1 <= s 的长度 <= 8

个人思路解析

方式一:回溯

这类涉及到全排列的问题,一般都会想到使用回溯来实现,通过交换元素位置来获取不同排列的字符串

class Solution {
    private Set<String> set;
    public String[] permutation(String s) {
        // 得到字符数组,方便元素交换位置
        char[] c = s.toCharArray();
        // 哈希集合去重
        set = new HashSet<>();
        // 回溯
        back(c, 0, "");
        // 返回结果
        String[] ans = new String[set.size()];
        return set.toArray(ans);
    }

    private void back(char[] arr, int index, String str){
        // 边界,将排序后的字符串添加到集合中
        if(index == arr.length){
            set.add(str);
            return;
        }

        // 遍历后续的元素,将每一位元素与 index 位置元素进行交换
        for(int i = index; i < arr.length; ++i){
            // 去重
            if(i != index && arr[i] == arr[index]){
                continue;
            }
            // 交换元素
            exchange(arr, index, i);
            
            back(arr, index + 1, str + arr[index]);
            // 恢复
            exchange(arr, index, i);
        }
    }

    private void exchange(char[] arr, int l, int r){
        char c = arr[l];
        arr[l] = arr[r];
        arr[r] = c;
    }
}

提交结果

image.png

方式二:下一个排列

通过将字符数组进行一个从小到大的排列,再重复去获取下一个比当前数组大的排列,可以有效避免出现重复的情况

例如:1, 3, 2

  1. 从后向前,找到非递增的首位元素 i, 此时 i = 0,当前元素为1
  2. 从后向前,找到首个比 c[i] 大的元素 2
  3. 交换两个元素的位置 2, 3, 1
  4. 同时将 i 后面的元素进行反转 2, 1, 3,这样即可得到最接近当前数组值的下一组数组

官方题解:leetcode-cn.com/problems/ne…

class Solution {
    public String[] permutation(String s) {
        List<String> list = new ArrayList<>();
        char[] c = s.toCharArray();
        // 排列数组
        Arrays.sort(c);
        do{
            // 添加到结果集
            list.add(new String(c));
        }while(nextPermutation(c));// 判断是否还有下一个排列

        // 返回结果
        String[] res = new String[list.size()];
        return list.toArray(res);
    }

    private boolean nextPermutation(char[] c){
        // 从后向前,找到非递增的首位元素
        int i = c.length - 2;
        while(i >= 0 && c[i] >= c[i + 1]){
            --i;
        }
        // 边界
        if(i < 0){
            return false;
        }
        
        // 从后向前,找到首个比 c[i] 大的元素
        int j = c.length - 1;
        while(j > i && c[i] >= c[j]){
            --j;
        }
        // 交换两个元素的位置
        swap(c, i, j);
        // 同时将 i 后面的元素进行反转
        reverse(c, i + 1);
        return true;
    }
    
    // 交换元素
    private void swap(char[] c, int i, int j){
        char temp = c[i];
        c[i] = c[j];
        c[j] = temp;
    }
    
    // 数组反转
    private void reverse(char[] c, int i){
        int j = c.length - 1;
        while(i < j){
            swap(c, i, j);
            ++i;
            --j;
        }
    }
}

提交结果

image.png