Leetcode每日刷题:回溯算法&(数组+字符串)

1.回溯算法:全排列

Leetcode每日刷题:回溯算法&(数组+字符串)_第1张图片

class Solution {
    List<List<Integer>> res=new LinkedList<>();
    public List<List<Integer>> permute(int[] nums) {
        if(nums==null ||nums.length==0){
            return res;
        }
        LinkedList<Integer> track=new LinkedList<>();
        backTrack(nums,track);
        return res;
    }
    public void backTrack(int[] nums,LinkedList<Integer> track){
        if(track.size()==nums.length){
            res.add(new LinkedList(track));
            return;
        }

        for(int i=0;i<nums.length;i++){
            if(track.contains(nums[i])){
                continue;
            }
            track.add(nums[i]);
            backTrack(nums,track);
            track.removeLast();
        }
    }
}

2.回溯算法:子集

Leetcode每日刷题:回溯算法&(数组+字符串)_第2张图片

class Solution {
    List<List<Integer>> res=new LinkedList<>();
    public List<List<Integer>> subsets(int[] nums) {
        if(nums==null || nums.length==0){
            return res;
        }
        LinkedList<Integer> track=new LinkedList<>();
        backTrack(nums,track,0);
        return res;
    }

    //剪枝:使用一个index索引限定每次只能继续向下寻找,不能往数组前面遍历,以免出现重复
    public void backTrack(int[] nums,LinkedList<Integer> track,int index){
        res.add(new LinkedList(track));

        for(int i=index;i<nums.length;i++){
            track.add(nums[i]);
            backTrack(nums,track,i+1);
            track.removeLast();
        }
    }
}

3.回溯算法:组合

Leetcode每日刷题:回溯算法&(数组+字符串)_第3张图片

class Solution {
    List<List<Integer>> res=new LinkedList<>();
    public List<List<Integer>> combine(int n, int k) {
        if(n==0 || k==0){
            return res;
        }
        LinkedList<Integer> track=new LinkedList<>();
        backTrack(n,k,track,1);
        return res;
    }
    public void backTrack(int n,int k,LinkedList<Integer> track,int index){
        if(track.size()==k){
            res.add(new LinkedList(track));
            return;
        }
        for(int i=index;i<=n;i++){
            track.add(i);
            backTrack(n,k,track,i+1);
            track.removeLast();
        }
    }
}

4.回溯算法:全排列2

Leetcode每日刷题:回溯算法&(数组+字符串)_第4张图片

class Solution {
    List<List<Integer>> res=new LinkedList<>();
    public List<List<Integer>> permuteUnique(int[] nums) {
        if(nums==null || nums.length==0){
            return res;
        }
        Arrays.sort(nums);//排序是剪枝的关键
        LinkedList<Integer> track=new LinkedList<>();
        boolean[] used=new boolean[nums.length];
        backTrack(nums,track,used);
        return res;
    }
    public void backTrack(int[] nums,LinkedList<Integer> track,boolean[] used){
        if(track.size()==nums.length){
            res.add(new LinkedList(track));
        }
        for(int i=0;i<nums.length;i++){
            if(used[i]){
                continue;
            }
            //剪枝,去除相同的值出现的分支
            if(i>0 && nums[i]==nums[i-1] && used[i-1]==false){
                continue;
            }
            track.add(nums[i]);
            used[i]=true;
            backTrack(nums,track,used);
            track.removeLast();
            used[i]=false;
        }
    }
}

5.回溯算法:括号生成

对于括号合法性的判断,主要是借助「栈」这种数据结构,而对于括号的生成,一般都要利用回溯递归的思想
Leetcode每日刷题:回溯算法&(数组+字符串)_第5张图片

class Solution {
    //采用回溯算法,列举出所有可能的字符串,从其中选出符合条件的字符串
    List<String> res=new LinkedList<>();
    public List<String> generateParenthesis(int n) {
        if(n<=0){
            return res;
        }
        StringBuilder sb=new StringBuilder();
        backTrack(n,n,sb);
        return res;
    }
    //left和right表示剩余可用的左括号数量和右括号数量
    public void backTrack(int left,int right,StringBuilder sb){
        //字符串中左括号数量一定大于等于右括号数量,所以当左括号剩余数量大于右括号时,生成的括号一定是不合法的
        if(left>right) return;
        //剩余个数小于0,说明所使用的左括号或者右括号超过了提供的数量,也不合法
        if(left<0 || right<0) return;
        //左右括号数量同时用完,满足条件
        if(left==0 && right==0){
            res.add(new String(sb.toString()));
            return;
        }

        //添加左括号并回溯
        sb.append('(');
        backTrack(left-1,right,sb);
        sb.deleteCharAt(sb.length()-1);

        //添加右括号并回溯
        sb.append(')');
        backTrack(left,right-1,sb);
        sb.deleteCharAt(sb.length()-1);
    }
}

6.回溯算法:划分为k个相等的子集

Leetcode每日刷题:回溯算法&(数组+字符串)_第6张图片

class Solution {
    public boolean canPartitionKSubsets(int[] nums, int k) {
        //排除基本情况:
        if(k>nums.length) return false;
        int sum=0;
        for(int i:nums){
            sum+=i;
        }    
        if(sum%k!=0) return false;

        //回溯法,将数组中的元素装进k个桶:
        //1.每个桶中的数值和
        int target=sum/k;
        //2.记录用过的数字,如果已经装进一个桶,则不能再装进别的桶
        boolean[] used=new boolean[nums.length];
        //3.调用回溯函数,后面两个0:一个是桶中数值和的初始值,一个是索引位置初始值
        return backTrack(nums,k,target,used,0,0);
    }
    public boolean backTrack(int[] nums,int k,int target,boolean[] used,int bucket,int start){
        //如果k为0,则说明已经将k个桶都装好了
        if(k==0) return true;
        //如果bucket==target,说明当前桶装好了,需要去判断下一个桶
        if(bucket==target){
            return backTrack(nums,k-1,target,used,0,0);
        }
        //当前桶操作
        for(int i=start;i<nums.length;i++){
            //如果其他桶用过,跳过
            if(used[i]) continue;
            //如果当前元素加上桶中元素和大于target,跳过
            if(nums[i]+bucket>target) continue;
            //将当前元素装入桶中
            bucket+=nums[i];
            used[i]=true;
            if(backTrack(nums,k,target,used,bucket,i+1)){
                return true;
            }
            bucket-=nums[i];
            used[i]=false;
        }
        //穷举了所有数字,都无法装满当前桶
        return false;
    }
}

每个桶要遍历n个数字,选择「装入」或「不装入」,组合的结果有2n种;而我们有k个桶,所以总的时间复杂度为O(k*2n)。

你可能感兴趣的:(每日刷题,leetcode)