[Algorithm]九章五.DFS

做DFS的题,想象构造树非常关键,把树构造的过程梳理清楚了,自然递归的步骤也就出来了。

17. Subsets:  点击打开链接

思路:对于[1,2,3]

             [ ]

         [1]  这边的1是在[ ]的基础上添加的

    [1,2]    [1,3]  所以倒回来到[1,3]的时候也应该知道是在[1]的基础上添加的

[1,2,3]  因为这层是在[1,2]的基础上添加的3

public class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> result=new ArrayList<>();
        if(nums==null || nums.length==0){
            return result;
        }
        Arrays.sort(nums);
        helper(result,new ArrayList<>(),nums,0);
        return result;
    }
    
    private void helper(List<List<Integer>> result,List<Integer> list,int[] nums,int start){      
        result.add(new ArrayList<>(list));
        
        for(int i=start;i<nums.length;i++){
            list.add(nums[i]);
            helper(result,list,nums,i+1);
            list.remove(list.size()-1);
        }
    }
}

18. Sebsets II: 点击打开链接

class Solution {
    /**
     * @param nums: A set of numbers.
     * @return: A list of lists. All valid subsets.
     */
    public ArrayList<ArrayList<Integer>> subsetsWithDup(int[] nums) {
       ArrayList<ArrayList<Integer>> results=new ArrayList<>();
       ArrayList<Integer> list=new ArrayList<>();
       
       Arrays.sort(nums);
       
       if(nums==null){
           return results;
       }
       if(nums.length==0){
           results.add(list);
           return results;
       }
       
       helper(nums,0,results,list);
       
       return results;
    }
    
    private void helper(int[] nums, int startIndex,ArrayList<ArrayList<Integer>> results,ArrayList<Integer> list){
        results.add(new ArrayList<>(list));
        
        for(int i=startIndex;i<nums.length;i++){              //此处if语句也说明了之前数组排序的重要性
            if(i!=0 && nums[i]==nums[i-1] && i>startIndex){   //首先要防止数组角标越界:i!=0
                continue;                                     //比较前后两个数是针对后面再一次出现同一个数的情况
            }                                                 //如果前一个同样的数还没有放进去过,就不能放后一个同样的数;
            list.add(nums[i]);                                
            helper(nums,i+1,results,list);
            list.remove(list.size()-1);
        }
    }
}

135.Combination Sum:   点击打开链接

1.   [2]

 -> [2,2]

 -> [2,2,2]

 -> [2,2,2,2], 判断和大于target,后面的[2,2,2,3],[2,2,2,6],[2,2,2,7]都不用再判断,break。

2.  回到i++,也就是外一圈的helper,[2,2,3]里判断,此时正好等于target,return。

     判断[2,2,6],和大于target,后面的整个[2,2,7]都垮掉。

3. 再回到外一圈的helper,[2,3]里判断,以此类推。

4. 直到以[2]开头的都判断完,回到最外层helper,再判断以[3]开头的。

注意:当一个元素可以用很多次,结果又不能有重复list,就要取出原数组的重复元素

public class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result=new ArrayList<>();
        List<Integer> list=new ArrayList<>();
        
        if(candidates==null || candidates.length==0){
            return result;
        }
        
        int[] nums=removeDuplicates(candidates);
        helper(nums,target,0,0,result,list);
        return result;
    }
    //递归的定义:寻找所有以list开头的满足条件的组合,放到result里
    private void helper(int[] nums,int target,int startIndex,int sum,List<List<Integer>> result,List<Integer> list){
        if(sum==target){                                    //递归的出口
            result.add(new ArrayList<Integer>(list));
            return;
        }
        
        for(int i=startIndex;i<nums.length;i++){            //这里的for循环执行两种功能,如果可以继续加值,里层的helper用的
            if(sum+nums[i]>target){                         //还有就是remove最后一个元素后,同一层的下一个组合用的
                break;                                      //一旦break,跳出整体for循环,并不是本层for循环
            }
            list.add(nums[i]);
            helper(nums,target,i,sum+nums[i],result,list);  //i的取值也很讲究:只要是元素可以重复使用,下次取的时候还是位置i
            list.remove(list.size()-1);                     
        }
    }
    private int[] removeDuplicates(int[] candidates){       //去重是必要的,不然result里可能会有重复小list
        Arrays.sort(candidates);
        ArrayList<Integer> list=new ArrayList<>();
        for(int i=0;i<candidates.length;i++){
            if(!list.contains(candidates[i])){
                list.add(candidates[i]);
            }
        }
        
        int[] nums=new int[list.size()];
        for(int i=0;i<list.size();i++){
            nums[i]=list.get(i);
        }
        return nums;
    }
}

 

public class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result=new ArrayList<>();
        List<Integer> list=new ArrayList<>();
        if(candidates==null || candidates.length==0){
            return result;
        }
        int[] nums=removeDuplicates(candidates);
        helper(nums,target,0,0,result,list);
        return result;
    }
    
    private void helper(int[] nums,int target,int startIndex,int sum,List<List<Integer>> result,List<Integer> list){
        if(sum==target){
            result.add(new ArrayList<Integer>(list));
            return;
        }
        
        for(int i=startIndex;i<nums.length;i++){
            if(sum<target){                                   
                list.add(nums[i]);
                helper(nums,target,i,sum+nums[i],result,list);
                list.remove(list.size()-1);
            }
        }
    }
    
    private int[] removeDuplicates(int[] nums){
        Arrays.sort(nums);
        int index=0;
        for(int i=0;i<nums.length;i++){
            if(nums[i]!=nums[index]){
                index++;
                nums[index]=nums[i];
            }
        }
        int[] result=new int[index+1];
        for(int i=0;i<result.length;i++){
            result[i]=nums[i];
        }
        return result;
    }
}

153. Combination Sum II :   点击打开链接

注意:当一个元素只可以用一次,原数组里又有重复元素,结果又不能有重复list,就要判断后面的元素是不是先于前面的元素被使用

public class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> result=new ArrayList<>();
        List<Integer> list=new ArrayList<>();
        if(candidates==null || candidates.length==0){
            return result;
        }
        Arrays.sort(candidates);
        helper(candidates,target,0,0,result,list);
        return result;
    }
    
    private void helper(int[] nums,int target,int startIndex,int sum,List<List<Integer>> result,List<Integer> list){
        if(sum==target){
            result.add(new ArrayList<Integer>(list));
            return;
        }
        
        for(int i=startIndex;i<nums.length;i++){
            if(sum<target){
                if(i!=0 && nums[i]==nums[i-1] && i!=startIndex){        //因为i-1等于startIndex
                    continue;                                           //nums[i-1]没有被拿过,就不能拿nums[i]
                }
                list.add(nums[i]);
                helper(nums,target,i+1,sum+nums[i],result,list);
                list.remove(list.size()-1);
            }
        }
    }  
}

136. Palindrome Partitioning:  点击打开链接

public class Solution {
    /**
     * @param s: A string
     * @return: A list of lists of string
     */
    public List<List<String>> partition(String s) {
        List<List<String>> result=new ArrayList<>();
        List<String> list=new ArrayList<>();
        
        if(s==null || s.length()==0){
            return result;
        }
        
        helper(s,0,result,list);
        
        return result;
    }
    
    private void helper(String s,int startIndex,List<List<String>> result,List<String> list){
        if(startIndex==s.length()){
            result.add(new ArrayList<>(list));
            return;
        }
        
        for(int i=startIndex;i<s.length();i++){
            String sub=s.substring(startIndex,i+1);
            if(!isPalindrome(sub)){
                continue;                                    //并不是最大层for循环整体垮掉,只是终止本层for循环
            }
            list.add(sub);
            helper(s,i+1,result,list);
            list.remove(list.size()-1);
        }
    }
    
    private boolean isPalindrome(String s){
        for(int i=0, j=s.length()-1; i<j ; i++, j--){
            if(s.charAt(i)!=s.charAt(j)){
                return false;
            }
        }
        return true;
    }
}

267. Palindrome Partitioning II 

Given a string s, return all the palindromic permutations (without duplicates) of it. Return an empty list if no palindromic permutation could be form.

Example 1:

Input: "aabb" Output: ["abba", "baab"]

Example 2:

Input: "abc" Output: []

class Solution {
    
    private Map<Character, Integer> map = new HashMap<>();
    private List<String> results = new ArrayList<>();
    private String middleChar = "";
    
    public List<String> generatePalindromes(String s) {
        if(s == null || s.length() == 0)
        {
            return results;
        }

        boolean bFormed = canBeFormed(s);       
        if(bFormed)
        {
            helper(results, s, middleChar);
        }
        return results;        
    }
       
    private void helper(List<String> results, String s, String tempS)
    {
        if(tempS.length() == s.length())
        {
            results.add(tempS);
            return;
        }
        for(Character c: map.keySet())
        {
            int countForVal = map.get(c);
            if(countForVal >= 2)
            {
                map.put(c, countForVal - 2);
                helper(results, s, c+tempS+c);
                map.put(c, countForVal);
            }          
        }
    }
    
    // 不要受I的思路影响, 判断是否可以形成Palindrome非常关键
    // 如果一个字符串里的字符是奇数的个数大于1,就完全形不成,如果只有1个字符的个数是奇数,把那个的一个放在最中间即可
    private boolean canBeFormed(String s)
    {                
        char[] c = s.toCharArray();
        for(int i=0; i<c.length; i++)
        {
            map.put(c[i], map.getOrDefault(c[i],0)+1);
        }
        
        for(Character k: map.keySet()){
            int count = map.get(k);
            if(count%2 != 0){		
                if(middleChar != "")
                {
                   return false; 
                } 
                middleChar = k+"";               
            }           
        }
        return true;
    }            
}

15. Permutations:  点击打开链接

public class Solution {
    public List<List<Integer>> permute(int[] nums) {                            //recursion方法一
        List<List<Integer>> result=new ArrayList<>();
        if(nums==null || nums.length==0){
            return result;
        }
        
        int[] visited=new int[nums.length];
        for(int i=0;i<nums.length;i++){
            visited[i]=0;
        }
        helper(result,new ArrayList<>(),nums,visited);
        return result;
        
    }
    
    private void helper(List<List<Integer>> result,List<Integer> list,int[] nums,int[] visited){
        if(list.size()==nums.length){
            result.add(new ArrayList<>(list));
            return;
        }
        
        for(int i=0;i<nums.length;i++){
            if(visited[i]==1){ 
                continue;
            }
            list.add(nums[i]);
            visited[i]=1;
            helper(result,list,nums,visited);
            list.remove(list.size()-1);
            visited[i]=0;           
        }
    }
}
class Solution {                                                                //recursion方法二
    /**
     * @param nums: A list of integers.
     * @return: A list of permutations.
     */
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> result=new ArrayList<>();
        List<Integer> list=new ArrayList<>();
        
        if(nums==null){
            return result;
        }
        
        if(nums.length==0){
            result.add(list);
            return result;
        }
        
        helper(result,list,nums);
        return result;
    } 
    
    
    private void helper(List<List<Integer>> result,
                        List<Integer> list,
                        int[] nums){
        if(list.size()==nums.length){
            result.add(new ArrayList<>(list));
            return;
        }
        
        for(int i=0;i<nums.length;i++){
            if(list.contains(nums[i])){
                continue;
            }
            
            list.add(nums[i]);
            helper(result,list,nums);
            list.remove(list.size()-1);
        }
    }
}

16. Permuations II :  点击打开链接

class Solution {
    /**
     * @param nums: A list of integers.
     * @return: A list of permutations.
     */
    
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> result=new ArrayList<>();
        List<Integer> list=new ArrayList<>();
        
        if(nums==null){
            return result;
        }
        
        if(nums.length==0){
            result.add(list);
            return result;
        }
        
        Arrays.sort(nums);
        
        int[] visited=new int[nums.length];
        for(int i=0;i<visited.length;i++){
            visited[i]=0;
        }
        
        helper(result,list,nums,visited);
        return result;
    }
    
    private void helper(List<List<Integer>> result,
                        List<Integer> list,
                        int[] nums,
                        int[] visited){
        if(list.size()==nums.length){
            result.add(new ArrayList<>(list));
            return;
        }
        
        for(int i=0;i<nums.length;i++){
            if(visited[i]==1 || (i!=0 && nums[i]==nums[i-1] && visited[i-1]==0)){  
                continue;    //如果有相同元素的情况下,如果前面的元素还没有使用的时候,就不能让后面这个元素使用
            }                //也就是轮到这个元素的时候,它已经被放进去了,或者它前面的元素还没有被放进去
            
            list.add(nums[i]);
            visited[i]=1;
            helper(result,list,nums,visited);
            list.remove(list.size()-1);
            visited[i]=0;
        }
    }
}

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

For example, given n = 3, a solution set is:

[
  "((()))",
  "(()())",
  "(())()",
  "()(())",
  "()()()"
]
​
class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> results = new ArrayList<>();
        helper(results, n, 0, 0, new StringBuilder());
        return results;
    }
    
    private void helper(List<String> results, int n, int left, int right, StringBuilder tempS)
    {        
        int length = tempS.length();
        
        if(left == n && right == n)
        {
            results.add(tempS.toString());
            return;
        }
        
        if(left < right || left > n || right > n)  // 如果右半括号多于左半括号, EX.)()(
        {                                          // 或者任意面半括号数量多于给定个数,都是不符合条件的情况
            return;
        }
        
        helper(results, n, left+1, right, tempS.append("("));  
        tempS.setLength(length);
        
        helper(results, n, left, right+1, tempS.append(")"));
        tempS.setLength(length);
    }
}

33. N-Queens:  点击打开链接

1.同的一行不能存在两个皇后,但是又正好是N个皇后又要放在N行上,这就意味着每一行都有且仅有一个皇后,于是我们可以按照这样的方式来枚举这道题目的所有方案:依次枚举每一行皇后的位置,在这个枚举过程中确保不会在每一列、每一条斜线上出现两个皇后,对于行里面的每一个元素,都要遍历一次这个元素对应的一整列。

2.以[1,3,0,2]的ArrayList为例,也就是arrayList.get(0)=1, arrayList.get(1)=3, arrayList.get(2)=0, arrayList.get(3)=2

 

Q
Q
Q
Q

3.明确造成斜线攻击的条件:同一个斜线上的坐标之差相等,反方向上是同一个斜线上坐标之和相等。

class Solution {
    /**
     * Get all distinct N-Queen solutions
     * @param n: The number of queens
     * @return: All distinct solutions
     * For example, A string '...Q' shows a queen on forth position
     */
    ArrayList<ArrayList<String>> solveNQueens(int n) {
        ArrayList<ArrayList<String>> result=new ArrayList<>();
        ArrayList<Integer> cols=new ArrayList<>();
        if(n<=0){
            return result;
        }
        search(result,cols,n);
        return result;
    }
    
    private void search(ArrayList<ArrayList<String>> result, 
                        ArrayList<Integer> cols,
                        int n){
        if(cols.size()==n){
            result.add(drawChessBoard(cols));
            return;
        }
        
        for(int colIndex=0;colIndex<n;colIndex++){
            if(!isValid(cols,colIndex)){
                continue;
            }
            cols.add(colIndex);
            search(result,cols,n);
            cols.remove(cols.size()-1);
        }
    }
    
    private boolean isValid (ArrayList<Integer> cols,int column){       //判断有效落子位置
        int row=cols.size();
        for(int rowIndex=0;rowIndex<cols.size();rowIndex++){            //行里面的每一个元素,都要遍历一次这个元素对应的一整列
            if(cols.get(rowIndex)==column){                             //造成列攻击
                return false;
            }
            if(rowIndex+cols.get(rowIndex)==row+column){                //造成斜线攻击
                return false; 
            }
            if(rowIndex-cols.get(rowIndex)==row-column){                //造成斜线攻击
                return false; 
            }
        }
        return true;
    }
    
    private ArrayList<String> drawChessBoard(ArrayList<Integer> cols) { //每一个solution画成棋盘
        ArrayList<String> solution = new ArrayList<>();
        
        for (int i = 0; i < cols.size(); i++) {
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < cols.size(); j++) {
                if(j == cols.get(i)){
                    sb.append('Q');
                }else{
                    sb.append('.');
                }
            }
            solution.add(sb.toString());
        }
        return solution;        //最后以这样的形式表示棋盘[.Q.., ...Q, Q..., ..Q.], 以[1,3,0,2]为例
    }
}

34.N-Queens II :  点击打开链接

N-QueensII相对于I其实是更简单了,不让打印满足条件的棋盘,而是输出满足条件的棋盘个数。

class Solution {
    /**
     * Calculate the total number of distinct N-Queen solutions.
     * @param n: The number of queens.
     * @return: The total number of distinct solutions.
     */
    
    public int sum;
    public int totalNQueens(int n) {
       sum=0;
       search(new ArrayList<Integer>(), n);
       return sum;
    }
    
    private void search(ArrayList<Integer> solution,int n){    //注意这边不用初始化sum=0;
        if(solution.size()==n){
            sum++;
            return;
        }
        
        for(int colIndex=0; colIndex<n; colIndex++){
            if(!isValid(solution,colIndex)){
                continue;
            }          
            solution.add(colIndex);
            search(solution,n);
            solution.remove(solution.size()-1);
        }
    }
    
    private boolean isValid(ArrayList<Integer> solution,int column){
        int row=solution.size();
        for(int rowIndex=0;rowIndex<solution.size();rowIndex++){
            if(solution.get(rowIndex)==column){
                return false;
            }
            if(rowIndex+solution.get(rowIndex)==row+column){
                return false;
            }
            if(rowIndex-solution.get(rowIndex)==row-column){
                return false;
            }
        }       
        return true;
    }
};

12.Min Stack:点击打开链接

public class MinStack {
    
    Stack<Integer> stack=new Stack<>();
    Stack<Integer> minStack=new Stack<>();
    
    public MinStack() {
        // do initialize if necessary
    }

    public void push(int number) {
        stack.push(number);
        if(minStack.isEmpty() || minStack.peek()>=number){ //如果栈是空的,可以直接放,如果栈里有就要比较与加入number的大小
            minStack.push(number);                         //只有原来peek的比number的值大至少等于number,才可以加入number
        }                                                  //也就是说,如果要加入的一直比minStack.peek()的大,就一直不加入minStack里
    }

    public int pop() {
        int x=stack.pop();
      
        if(minStack.peek()==x){                            //如果当初加入的时候加入的比minStack原有的大,则minStack不加入
            minStack.pop();                                //这时pop的时候minStack也不pop
        }                                                  
        return x;
    }

    public int min() {
        return minStack.peek();
    }
}

575.Expression Expand:  点击打开链接

public class Solution {
    /**
     * @param s  an expression includes numbers, letters and brackets
     * @return a string
     */
    public String expressionExpand(String s) {
        Stack<Object> stack=new Stack<>();
        int num=0;
        
        for(char c : s.toCharArray()){                //c只有4种:数字, '[', ']'或者字符串
            if(Character.isDigit(c)){                 //如果是数字,就拿到数字
                num=num*10+c-'0';
            }else if(c=='['){                         //如果是'[',就把数字入栈
               stack.push(Integer.valueOf(num));      //数字入栈之后要归零,后面的数字还要在for循环里出现
               num=0;                                 
            }else if(c==']'){                         //如果是']',就要拿出[]所包含的字符串,并让之前相应的数字出栈
                String curString=popStack(stack);     
                Integer count=(Integer)stack.pop();
                for(int i=0;i<count;i++){             //有几个字符串,就以for循环的形式入栈几次
                    stack.push(curString);            //解决了形如 3[abc]这样的问题
                }
            }else{                                    //如果是字符串,就直接入栈
                stack.push(String.valueOf(c));
            }
        }
        return popStack(stack);                       //最后最大括号的整体也要当做字符串
    } 
    
    private String popStack(Stack<Object> stack){     //将stack里的字符串出栈,导入暂时栈temp,然后添加到sb里转化为String
        Stack<String> tempStack=new Stack<>();
        String str=null;
        while(!stack.isEmpty() && stack.peek() instanceof String){
            tempStack.push((String) stack.pop());
        }
        
        StringBuilder sb=new StringBuilder();
        while(!tempStack.isEmpty()){
            sb.append(tempStack.pop());
        }
        return sb.toString();
    }
}

433.Numbers of Islands: 点击打开链接

对于每一个grid[i][j]点进行遍历, 如果是小岛,即grid[i][j] == true,则result+1,并对四个方向进行DFS查找(查找的时候要先判断有效点,也就是坐标加减后还在给定矩阵中的点),并将所有属于那坐岛屿的点标记为非岛屿。这样遍历过的地方全部会变成非岛屿,而岛屿的数量已被记录。(标记的意义在于:不会对同一块已经计数过的小岛反复计数)。

public class Solution {
    /**
     * @param grid a boolean 2D matrix
     * @return an integer
     */
    public int numIslands(boolean[][] grid) {                        //写法一
        if(grid==null || grid.length==0){
            return 0;
        }
        
        int result=0;
        for(int i=0;i<grid.length;i++){
            for(int j=0;j<grid[0].length;j++){
                if(grid[i][j]==true){
                    result++;
                    dfs(grid,i,j);
                }
            }
        }
        return result;
    }
    
    private void dfs(boolean[][] grid,int x,int y){                  //找grid[i][j]四周为岛屿的点
        int[] dx={0,0,-1,1};                                         //如果有就把它们标记为false,表示和grid[i][j]连接
        int[] dy={-1,1,0,0};
        if(isValid(grid,x,y) && grid[x][y]==true){
            grid[x][y]=false;
            for(int k=0;k<4;k++){
                dfs(grid,x+dx[k],y+dy[k]);
            }
        }
    }
    
    private boolean isValid(boolean[][] grid,int x,int y){
        if(x<0 || x>=grid.length){
            return false;
        }
        if(y<0 || y>=grid[0].length){
            return false;
        }
        return true;
    }
}

 

public class Solution {
    /**
     * @param grid a boolean 2D matrix
     * @return an integer
     */
    public int numIslands(boolean[][] grid) {                          //写法二,思路是一样的
        if(grid==null || grid.length==0){
           return 0; 
        }
      
        int result=0;
        for(int i=0;i<grid.length;i++){
            for(int j=0;j<grid[0].length;j++){
                if(grid[i][j]==true){
                   result++;
                   dfs(grid,i,j);
                }
            }
        }
        return result;
    }
    
    private void dfs(boolean[][] grid, int i, int j){
        if(i<0 || i>=grid.length || j<0 || j>=grid[0].length){
            return;
        }
       
        if(grid[i][j]==true){
            grid[i][j]=false;
            dfs(grid,i+1,j);
            dfs(grid,i-1,j);
            dfs(grid,i,j+1);    
            dfs(grid,i,j-1);
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值