刷题日记07《回溯算法》

题目描述

力扣https://leetcode.cn/problems/VvJkup/

给定一个不含重复数字的整数数组 nums ,返回其 所有可能的全排列 。可以 按任意顺序 返回答案。

示例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:

输入:nums = [0,1]
输出:[[0,1],[1,0]]
示例 3:

输入:nums = [1]
输出:[[1]]

 

提示:

1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同

解题思路

与排列不同的是,组合不需要保持元素间的相对位置保持不变(元素位置调换是一种新的组合),因此对于每一层的递归中,都需要从头开始选择元素加入备选数据中,但是data中可能存在重复元素,因此我们需要使用一个choose数组来判断该元素是否已经被选择, 同时使用下标index作为递归结束的指标

实例代码

class Solution {
    //存放元素的队列
     private List<List<Integer>>res=new LinkedList<>();
    private LinkedList<Integer>data=new LinkedList<>();
    //存放元素是否选择的数组
    boolean isChoose[];
    public List<List<Integer>> permute(int[] nums) {
        //初始化选择数组
        isChoose=new boolean[nums.length];
        reverse(nums,0);
        return res;
    }
    //递归函数
    public void reverse(int[]nums,int index){
        //递归出口
        if(index==nums.length){
            res.add(new LinkedList(data));
        }
        //遍历添加
        for(int i=0;i<nums.length;++i){
            if(isChoose[i]){
                continue;
            }
            data.add(nums[i]);
            isChoose[i]=true;
            //递归
            reverse(nums,index+1);
            //回溯
            isChoose[i]=false;
            data.removeLast();
        }
    }
}

题目描述

力扣https://leetcode.cn/problems/7p8L0Z/

给定一个可包含重复数字的整数集合 nums ,按任意顺序 返回它所有不重复的全排列。

示例 1:

输入:nums = [1,1,2]
输出:
[[1,1,2],
 [1,2,1],
 [2,1,1]]
示例 2:

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

 

提示:

1 <= nums.length <= 8
-10 <= nums[i] <= 10

解题思路

与上题有所不同的是,num数组中存在重复的元素,但是重复的元素在进行组合过程中会产生重复结果,因此我们需要进行剪枝处理,我们的剪枝策略如下:如果nums[i]==nums[i-1]相等,并且当前data中没有选择nums[i-1]但是选择了nums[i],将这种情况进行去除(因为前面没选择但是后面选择了说明前面元素已经选择过了),同样需要一个index,当index指向最后元素时,递归结束

实例代码

class Solution {
     //在求无相同元素的基础上加之排序将问题解决
        //存放元素的队列
     private List<List<Integer>>res=new LinkedList<>();
    private LinkedList<Integer>data=new LinkedList<>();
    //存放元素是否选择的数组
    boolean isChoose[];
    public List<List<Integer>> permuteUnique(int[] nums) {
       isChoose=new boolean[nums.length];
       //排序
       Arrays.sort(nums);
       reverse(nums,0);
       return res;
    }
    //递归函数
    public void reverse(int[]nums,int index){
        //递归出口
        if(index==nums.length){
            //加入结果
            res.add(new LinkedList(data));
        }
        //循环加入、
        for(int i=0;i<nums.length;++i){
            if(isChoose[i]){
                //剪枝1
                continue;
            }
            if(i>0&&nums[i]==nums[i-1]&&!isChoose[i-1]){
                //剪枝2
                continue;
            }
            //结果加入数据
            data.add(nums[i]);
            isChoose[i]=true;
            //递归
            reverse(nums,index+1);
            //回溯
            isChoose[i]=false;
            data.removeLast();
        }
    }
}

题目描述

力扣icon-default.png?t=N658https://leetcode.cn/problems/M99OJA/

正整数 n 代表生成括号的对数,请设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:

输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:

输入:n = 1
输出:["()"]

 

提示:

1 <= n <= 8

解题思路

本题为括号问题,面对这种题目,如果递归随意组合会产生很多错误的结果,所以必须对其进行合适的剪枝,而剪枝策略也比较简单,当左括号的个数小于右括号或者当左括号或者右括号的个数大于n的时候,直接进行剪枝即可,将最后符合要求的结果放入结果集即可

实例代码

class Solution {
    private List<String>res=new LinkedList<>();
    private char[] data=null;
    public List<String> generateParenthesis(int n) {
        data=new char[2*n];
        reverse(n,0,0);
        return res;
    }
    //递归函数
    public void reverse(int n,int leftCount,int rightCount){
     if(leftCount>n||rightCount>n||leftCount<rightCount){
         return;
     }
        if(leftCount==n&&rightCount==n){
            res.add(new String(data));
            return;
        }
        //处理
        data[leftCount+rightCount]='(';
        reverse(n,leftCount+1,rightCount);
        data[leftCount+rightCount]=')';
        reverse(n,leftCount,rightCount+1);
    }

题目描述

力扣icon-default.png?t=N658https://leetcode.cn/problems/palindromic-substrings/

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

示例 1:

输入:s = "abc"
输出:3
解释:三个回文子串: "a", "b", "c"
示例 2:

输入:s = "aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"

 

提示:

1 <= s.length <= 1000
s 由小写英文字母组成

解题思路

使用动态规划对此题进行求解,创建二维数组dp[i][j],其含义为从i到j该字符串是否为回文串,而判断回文子串的规则为:如果j-i<=1且char[i]==char[j] ,表示两个字符为一个字符或者两个字符串相连,此时两者为组成的字符串为回文子串,如果j-i>1且char[i]==char[j],若dp[i+1][j-1]=true,则说明从i到j组成的字符串为回文串,初始化,因为没有验证是否为回文串,每个字符串刚开始全部为false,遍历顺序-> dp[i][j]=dp[i+1][j-1],所以遍历顺序为从下到上,从左向右进行遍历

实例代码

class Solution {
    public int countSubstrings(String s) {
        int res=0;
         //直接使用动态规划进行求解
        int n=s.length();
        boolean dp[][]=new boolean[n][n];
        //将s转化为数组
        char[]ch=s.toCharArray();
        for(int i=n-1;i>=0;--i){
            for(int j=i;j<n;++j){
                if(ch[i]==ch[j]){
                    if(j-i<=1){
                        dp[i][j]=true;
                        res++;
                    }else{
                        if(dp[i+1][j-1]==true){
                            dp[i][j]=true;
                            res++;
                        }
                    }
                }
            }

        }
        return res;
    }
}

猜你喜欢

转载自blog.csdn.net/m0_65431718/article/details/131599219