DFS实践与练习(多题警告~附完整代码和思路)

牛客第27题

  • 输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

解题思路:

  • 1、截止条件
  • 2、遍历候选节点
  • 2.1、筛选候选节点
void dfs(int[] p, boolean[] pb, StringBuilder sb, List<List<String>> res){
	//1 截止条件
	if(p.length == sb.legth()){
		res.add(sb.toString());
		return;
	}
	//2 遍历候选节点
	for(int i = 0; i < p.length; i ++){
		int c = p[i];
		if(!pb[i]){		//2.1 筛选候选节点
			pb[i] = true;
			sb.append(c);
			dfs(p, pb, sb, res);
			pb[i] = false;
			sb.delete(sb.length() - 1, length());
		}
	}
}
import java.util.*;
public class Solution {
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> res = new ArrayList<String>();
        if(str.equals("") || str.length()==0)return res;
        StringBuilder s = new StringBuilder(str);
        dfs(s,0,new StringBuilder(),res);
        return res;
    }
    void dfs(StringBuilder s,int index, StringBuilder sb, ArrayList<String> res){
        //判断
        if(s.length() == index){
            if(!res.contains(sb.toString())){
                res.append(sb.add(sb.toString()));
            }
            return;
        }
        for(int i = 0; i < s.length(); i ++){	//候选元素
            char c = s.charAt(i);
            if(c != '0'){		//判断是否被访问过
                s.setCharAt(i, '0');
                sb.a(c);
                dfs(s, index + 1, sb, res);
                sb.deleteCharAt(sb.length()-1);
                s.setCharAt(i, c);   
            }   
        }   
    }
}

leetcode17. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
img
示例:

输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。

import java.util.*;
class Solution {
    char[][] m = {{},{},{'a', 'b', 'c'}, {'d', 'e', 'f'},   
    {'g', 'h', 'i'}, {'j', 'k', 'l'}, {'m', 'n', 'o'},
    {'p', 'q', 'r', 's'},{'t', 'u', 'v'},
    {'w', 'x', 'y', 'z'}}; //0和1是空的,值是从2开始
    

    public List<String> letterCombinations(String str) {
        List<String> res = new ArrayList<>();
        if(str.equals("")){
            return res;
        }
        dfs(str,0,new StringBuilder(),res);
        return res;
    }

    void dfs(String str,int index,StringBuilder sb, List<String>res){
        // 1、截止条件
        if (index == str.length()){
            res.add(sb.toString());
            return;
        }
        // 2、候选节点
        for(char c : m[str.charAt(index) - '0']){
            sb.append(c);						//保存节点数据
            dfs(str,index + 1, sb, res);		//递归
            sb.deleteCharAt(sb.length() - 1);	//恢复现场
        }

    }

} 

leetcode39. 组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

  • 所有数字(包括 target)都是正整数。
  • 解集不能包含重复的组合。
    示例 1:
输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]

示例 2:

输入:candidates = [2,3,5], target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]

提示:

1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都是独一无二的。
1 <= target <= 500
import java.util.*;
class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        dfs(candidates,target,new ArrayList<>(),res);
        return res;
    }
    void dfs(int[] c, int t, List<Integer> chaim, List<List<Integer>> res){
        //1、截止条件
        int s = sum(chaim);
        if(s >= t){
            if(s == t){
                List<Integer> tmp = new ArrayList<>(chaim);
                Collections.sort(tmp);
                if(!res.contains(tmp)){
                    res.add(tmp);
                }
            }
            return;
        }
        //2、候选节点
        for(int i = 0; i < c.length; i ++){
            int temp = c[i];
            chaim.add(temp);
            dfs(c, t, chaim,res);
            chaim.remove(chaim.size() - 1);
        }
    }
    int sum(List<Integer> a){
        int sum = 0;
        for(Integer t : a){
            sum += t;
        }
        return sum;
    }
}
执行结果:通过
执行用时:
189 ms
, 在所有 Java 提交中击败了
5.00%
的用户
内存消耗:
40.6 MB
, 在所有 Java 提交中击败了
5.20%
的用户

可以看到上边的结果耗时比较久,这是因为有了这个求和的操作,我们可以省略求和,在dfs递归的参数上做文章

import java.util.*;
class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        dfs(candidates,target,new ArrayList<>(),res);
        return res;
    }
    void dfs(int[] c, int t, List<Integer> chaim, List<List<Integer>> res){
        //1、截止条件
        if(0 >= t){
            if(0 == t){
                List<Integer> tmp = new ArrayList<>(chaim);
                Collections.sort(tmp);
                if(!res.contains(tmp)){
                    res.add(tmp);
                }
            }
            return;
        }
        //2、候选节点
        for(int i = 0; i < c.length; i ++){
            int temp = c[i];
            chaim.add(temp);
            dfs(c, t - temp, chaim,res);
            chaim.remove(chaim.size() - 1);
        }
    }
}
执行结果:通过
执行用时:
78 ms
, 在所有 Java 提交中击败了
5.00%
的用户
内存消耗:
40.3 MB
, 在所有 Java 提交中击败了
16.36%
的用户

leetcode 46. 全排列

这题和牛客第27题很相似,只不过这里不是打印出来而是放到list中

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

输入: [1,2,3]
输出:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]
import java.util.*;
class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        boolean[] dp = new boolean[nums.length];
        dfs(nums, dp, new ArrayList<Integer>(), res);
        return res;
    }
    void dfs(int[] p, boolean[]dp, List<Integer> chain, List<List<Integer>> res){
        //1 截止条件
        if(p.length == chain.size()){
            res.add(new ArrayList(chain));
            return;
        }
        //2 候选节点
        for(int i = 0; i < p.length; i++){
            int c = p[i];
            if(!dp[i]){
                dp[i] = true;
                chain.add(c);
                dfs(p,dp,chain,res);
                chain.remove(chain.size() - 1);
                dp[i] = false;
            }
        }
    }
}
执行用时:
1 ms, 在所有 Java 提交中击败了99.76%的用户
内存消耗:
39.9 MB, 在所有 Java 提交中击败了71.61%的用户
import java.util.*;
class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        dfs(nums, new ArrayList<Integer>(), res);
        return res;
    }
    void dfs(int[] p, List<Integer> chain, List<List<Integer>> res){
        //1 截止条件
        if(p.length == chain.size()){
            res.add(new ArrayList(chain));
            return;
        }
        //2 候选节点
        for(int i = 0; i < p.length; i++){
            int c = p[i];
            if(p[i]!=65535){
                p[i] = 65535;
                chain.add(c);
                dfs(p,chain,res);
                p[i] = c;
                chain.remove(chain.size() - 1);
            }
        }
    }
}

去掉了标志数组之后,直接用传入的数组给某一个下标设置标志:

执行用时:
2 ms, 在所有 Java 提交中击败了81.73%的用户
内存消耗:40.1 MB, 在所有 Java 提交中击败了48.51%的用户

leetcode 47. 全排列2

给定一个可包含重复数字的序列,返回所有不重复的全排列。

示例:

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

解题思路

此DFS算法,关键是总结截止条件和候选节点!

代码

class Solution {
    public List<String> generateParenthesis(int n) {
        char[] p = new char[]{'(', ')'};
        int[] pb = new int[]{n, n};
        List<String> res = new ArrayList<>();
        dfs(n, p, pb, new StringBuilder(), res);
        return res;
    }
    void dfs(int n,char[] p, int[] pb, StringBuilder sb, List<String> res){
        //关键是截止条件和
        if(n*2 == sb.length()){
            res.add(new String(sb.toString()));
            return;
        }
        //总结候选节点;
        if(pb[0]>0){
            pb[0] --;
            sb.append(p[0]);
            dfs(n, p, pb, sb, res);
            sb.delete(sb.length() - 1, sb.length());
            pb[0] ++;
        }
        if(pb[1]>0 && pb[0] != pb[1]){
            pb[1] --;
            sb.append(p[1]);
            dfs(n, p, pb, sb, res);
            sb.delete(sb.length() - 1, sb.length());
            pb[1] ++;
        }
    }
}

LeetCode93题,IP地址复原

解题思路

还是使用DFS来做,只不过对候选元素的判断不同,不能大于256并且除了0本身之外不能以0开头。

代码

class Solution {
    public List<String> restoreIpAddresses(String s) {
        List<String> res = new ArrayList<>();
        if(s.equals("") || s.length() < 4 || s.length() >12){
            return res;
        }
        dfs(s, -1, 1, new StringBuilder(), res);
        return res;
    }

    void dfs(String s, int index, int level, StringBuilder sb, List<String> res){
        //1 截止条件
        if(level == 5 || index == s.length() - 1){
            if(level == 5  && index == s.length() - 1){
                res.add(sb.toString().substring(0,sb.length() - 1));
            }
            return;
        }
        // 2 候选节点
        for(int i = 1; i < 4; i++){ // 只能截取1到3个字符
            if(index + i + 1 >= s.length() + 1){
                continue;
            }
            String x = s.substring(index + 1,index + i + 1);
            //if(x.equals(""))continue;
            //2.1 筛选候选节点
            int num = Integer.parseInt(x);
            if(num <= 255 && (x.equals("0") || !x.startsWith("0"))){
                int len = sb.length();
                sb.append(x+".");
                dfs(s, index + i, level + 1, sb, res);
                sb.delete(len, len + x.length() + 1);
            }
        }
    }
}

或者把level去掉

class Solution {
    public List<String> restoreIpAddresses(String s) {
        List<String> res = new ArrayList<>();
        if(s.equals("") || s.length() < 4 || s.length() >12){
            return res;
        }
        dfs(s, -1, new StringBuilder(), res);
        return res;
    }

    void dfs(String s, int index, StringBuilder sb, List<String> res){
        //1 截止条件
        if(sb.length() == s.length() + 4 || index == s.length() - 1){//因为要是结果正确,会多出4个'.'
            if(sb.length() == s.length() + 4  && index == s.length() - 1){
                res.add(sb.toString().substring(0,sb.length() - 1));
            }
            return;
        }
        // 2 候选节点
        for(int i = 1; i < 4; i++){
            if(index + i + 1 >= s.length() + 1){
                continue;
            }
            String x = s.substring(index + 1,index + i + 1);
            //if(x.equals(""))continue;
            //2.1 筛选候选节点
            int num = Integer.parseInt(x);
            if(num <= 255 && (x.equals("0") || !x.startsWith("0"))){
                int len = sb.length();
                sb.append(x+".");
                dfs(s, index + i, sb, res);
                sb.delete(len, len + x.length() + 1);
            }
        }
    }
}

你可能感兴趣的:(技术类博客,教程)