深搜解决字符串和数组的全排列问题

全排列系列题目(深搜)

通过两个题目:数组的全排列和字符串的全排列总结一下利用深度优先搜索解决全排列问题的一种思路和代码编写方法。

力扣46:数组的全排列

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

示例:

输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

思路

定义递归函数backTrack(first,output)表示从左往右填数组填到first个位置,当前排列为output

  • 如果first==n,说明已经填到了最后,当前排列output已经是一种排列了,则将结果加入结果集
  • 如果first<n,则需要在没填过的数字中选择一个填入当前位置。然后再继续调用backTrack(first+1,output)

为了区分一个数是否已经被用过了,可以将这个数组划分为两个部分。以first为分界,小于first部分的已经是填好并且用过的数字,大于first的部分则是没用过的可以用于填数的数字。
为了达到以上的分组,在选择下标为i的数字填入first的时候,可以将first和i下标的数交换。比如,当情况如下所示:
nums:[2,5 | 8,9,10]
我们要填nums[2],并选择nums[4]填,可以交换他们,并使情况达到以下所示:
nums:[2,5,10 | 9,8]
这样,就使得以first为界将数组划分为被填过和没填过的两部分了。

基于深度优先搜索的思想,我们需要将所有没填过的数都填入first一次,并调用backTrack(first+1,output)。因此需要从first到len(数组长度)-1,每个数都交换一次并调用backTrack(first+1,output),然后再交换回来复原,并选择下一个数交换。

下面看代码:

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        
        int len = nums.length;
        if(len == 0) return new ArrayList<>();
        
        //构建拿去交换填写的初始数组
        ArrayList<Integer> output = new ArrayList<Integer>();
        for(int i=0;i<len;i++) {
            output.add(nums[i]);
        }

		//res用于存储填好的数组
		//从第一个数组开始填数组,调用回溯深搜函数backTrack
        List<List<Integer>> res = new ArrayList<>();
        backTrack(len,0,output,res);
        
        return res;
    }

    public void backTrack(int n, int first, ArrayList<Integer> output, List<List<Integer>> res) {

        if(first == n) {
            //res.add(output); 解答就会错误??
            res.add(new ArrayList<Integer>(output));
            return;
        }

        for(int i=first;i<n;i++) {
            Collections.swap(output, first, i);
            backTrack(n, first+1, output, res);
            Collections.swap(output,first,i);
        }
    }
}

力扣面试题38:字符串的全排列

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

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

示例:

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

输出全排列的思路与上面输出数组的全排列的思路基本相同,但值得注意的是,字符串中可能会含有相同的元素,因此需要适当的在深搜回溯的过程中“剪枝”。
当选择的新的字母已经在前面使用过的时候,即发生重复字母现象时,不进行操作,中断搜索。为了实现这个操作,可以建立一个字母集来记录已经使用过的字母。

下面看代码:

class Solution {
    public String[] permutation(String s) {
        char[] charS = s.toCharArray();
        List<String> res = new LinkedList<>();
        backTrack(charS,res,0,s.length());
        return res.toArray(new String[res.size()]);
    }

    public void backTrack(char[] s, List<String> res, int first, int len) {
        if(first == len-1) {
            String ans = new String(s);
            res.add(ans);
            return;
        }

        HashSet<Character> charUsed = new HashSet<>();
        for(int i=first;i<len;i++) {
            if(!charUsed.contains(s[i])){
                charUsed.add(s[i]);
                swap(s,first,i);
                backTrack(s,res,first+1,len);
                swap(s,first,i);
            }
        }
    }

    public void swap(char[] s, int m, int n) {
        char temp = s[m];
        s[m] = s[n];
        s[n] = temp;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值