回溯法-力扣刷题合集代码-java实现-正在更新中

  • 笨人是一个高考数学高分的女生,但是刷题一直效果欠佳,现在琢磨着用学高中数学的方法来学算法题,重大策略:同类题型找共同点,总结出模板以后每次套模板就好!
  • 以下题目都是用同一块板子来写的(更易于套模板),思路都是回溯法
全排列

力扣链接

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        int len = nums.length;
        if (len == 0) {
            return result;
        }
        List<Integer> x = new ArrayList<Integer>();
        boolean[] used = new boolean[len];
        dfs(nums, x, len, result, used);
        return result;
    }
    public void dfs(int[] nums, List<Integer> x, int len, List<List<Integer>> result, boolean[] used) {
        if (x.size() == len) {
            result.add(new ArrayList<>(x));
            // 注意这个地方一定要新开宇哥ArrayList把x附进去,如果是x的话
            //其实传进去的是x的一个引用,之后x再递归过程中不断变化,会让result变化
            return;
        }
        for (int j = 0; j < len; j++) {
            //直接使用一个used数组来表示是否使用(因为对应位置可以在used对应)
            if (!used[j]) {
                x.add(nums[j]);
                used[j] = true;
                dfs(nums, x, len, result, used);
                x.remove(x.size() - 1);
                //List如果要remove就可以直接用下标
                //如果想用内容的话会返回一个Boolean值,要用多态
                used[j] = false;
            }
        }
    }}

使用used数组,在排列问题中非常有用,因为它帮助跟踪哪些元素已被使用,确保每个元素在每个排列中只使用一次。这个方法很好地解决了排列问题,其中元素顺序不同也视为不同的排列。

ip地址

力扣链接

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Stack;

public class Solution {

    public List<String> restoreIpAddresses(String s) {
        int len = s.length();
        List<String> res = new ArrayList<>();
        // 如果长度不够,不搜索
        if (len < 4 || len > 12) {
            return res;
        }
//双向链表
        Deque<String> path = new ArrayDeque<>(4);
        //成功分割次数
        int splitTimes = 0;
        //len是剩余字符串的长度
        dfs(s, len, splitTimes, 0, path, res);
        return res;
    }

    /**
     * 判断 s 的子区间 [left, right] 是否能够成为一个 ip 段
     * 判断的同时顺便把类型转了
     *
     * @param s
     * @param left
     * @param right
     * @return
     */

    private int judgeIfIpSegment(String s, int left, int right) {
        //-1代表失败
        int len = right - left + 1;
        // 大于 1 位的时候,不能以 0 开头
        if (len > 1 && s.charAt(left) == '0') {
            return -1;
        }

        // 转成 int 类型
        int res = 0;
        for (int i = left; i <= right; i++) {
            res = res * 10 + s.charAt(i) - '0';
        }

        if (res > 255) {
            return -1;
        }
        return res;
    }

    private void dfs(String s, int len, int split, int begin, Deque<String> path, List<String> res) {
        if (begin == len) {//如果待处理的begin==len说明已经遍历完了
            if (split == 4) {//遍历完乐,如果已经分割了4段了,那就OK
                res.add(String.join(".", path));//把path用.来组合
            }
            return;
        }

        // 看到剩下的不够了,就退出(剪枝),len - begin 表示剩余的还未分割的字符串的位数
        //剩下的位数要么一位,要么3位
        if (len - begin < (4 - split) || len - begin > 3 * (4 - split)) {
            return;
        }
        //试图分出新的一段
        for (int i = 0; i < 3; i++) {
            if (begin + i >= len) {
                break;
            }

            int ipSegment = judgeIfIpSegment(s, begin, begin + i);
            if (ipSegment != -1) {
                // 在判断是 ip 段的情况下,才去做截取
                path.addLast(ipSegment + "");
                dfs(s, len, split + 1, begin + i + 1, path, res);
                path.removeLast();//回溯
            }
        }
    }
}

  • 使用start来作为递归的变化索引
求子集

力扣链接

  • 这道题方法非常简洁,使用的变量也很少,因为完全按照人脑自然求子集的方法去做,思路可以看注释
import java.util.ArrayList;
import java.util.List;

class Solution {
    public static List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        //直接建一个新的数组传给dfs作为(current)
        dfs(nums, 0, new ArrayList<>(), result);
        return result;
    }
  
    private static void dfs(int[] nums, int start, List<Integer> current, List<List<Integer>> result) {
    //不需要剪枝条件,直接把current放进result,最开始是空,之后分别1,(1,2),(1,2,3)——跳出for循环后结束一次dfs,然后remove了3,2,然后(1,3)……一直往后
        result.add(new ArrayList<>(current));
        for (int i = start; i < nums.length; i++) {
            current.add(nums[i]);
            dfs(nums, i + 1, current, result);
            current.remove(current.size() - 1);
        }
    }

    public static void main(String[] args) {
        int[] nums = {1, 2, 3};
        List<List<Integer>> subsets = subsets(nums);
        System.out.println(subsets);
    }
}

  • 这题很简单,几乎 不用剪枝
  • 这里和上面一样都是使用地址维数变化(start+i)的形式,因为求子集和上一题一样,不关注元素的顺序
组合总和

力扣链接

import java.util.ArrayList;
import java.util.List;

class Solution {
    public static List<List<Integer>> combinationSum(int[] candidates, int target) {
        // 返回的结果不变
        List<List<Integer>> result = new ArrayList<>();
        // 直接建一个新的数组传给dfs作为(current)
        dfs(candidates, target, 0, new ArrayList<>(), result, 0);
        return result;
    }

    private static void dfs(int[] candidates, int target, int start, List<Integer> current, List<List<Integer>> result,
            int sum) {
        // 先判断是不是已经可以加入
        if (sum == target) {
            result.add(new ArrayList<>(current));
            return;
        }
        if (sum > target)
            return;
        // if(sum+candidates[start]>target) return;
        // 开始遍历了
        for (int i = start; i < candidates.length; i++) {
            // 这里是试探可否加入新的
            if (sum + candidates[i] > target) {
                if (i == candidates.length - 1)
                    return;
                i++;
            }
            current.add(candidates[i]);
            sum += candidates[i];
            dfs(candidates, target, i, current, result, sum);
            sum -= current.get(current.size() - 1);
            current.remove(current.size() - 1);
        }
    }

}
  • 本题主要是借用sum来在递归中传下去
电话号码的字母组合

力扣链接

import java.util.*;

//本题最大的问题就是我以为有两层循环不知道如何使用回溯法
//其实主要需要使用回溯法(需要向下延伸的是选了List中的一个字符以后,需要看下一个数字的时候)
//其实可以使用indextOf(String,index)
class Solution {
    public List<String> letterCombinations(String digits) {
        Map<Character, List<Character>> hashtable = new HashMap<Character, List<Character>>();
        hashtable.put('2', new ArrayList<Character>((Arrays.asList('a', 'b', 'c'))));
        hashtable.put('3', new ArrayList<Character>((Arrays.asList('d', 'e', 'f'))));
        hashtable.put('4', new ArrayList<Character>((Arrays.asList('g', 'h', 'i'))));
        hashtable.put('5', new ArrayList<Character>((Arrays.asList('j', 'k', 'l'))));
        hashtable.put('6', new ArrayList<Character>((Arrays.asList('m', 'n', 'o'))));
        hashtable.put('7', new ArrayList<Character>((Arrays.asList('p', 'q', 'r', 's'))));
        hashtable.put('8', new ArrayList<Character>((Arrays.asList('t', 'u', 'v'))));
        hashtable.put('9', new ArrayList<Character>((Arrays.asList('w', 'x', 'y', 'z'))));

        char[] s = digits.toCharArray();
        int len = s.length;
        List<String> result = new ArrayList<String>();
        //这一段单独返回比较重要,否则过不了
        if (len == 0) {
            return result;
        }
        dfs(s, len, result, hashtable, 0, new StringBuffer());
        return result;
    }

    public void dfs(char[] s, int len, List<String> result, Map<Character, List<Character>> hashtable, int start,
            StringBuffer uu) {
        // 比如说2,3,所有2代表的和所有3代表的进行组合,感觉没有剪枝条件
        if (start == len) {
            String q = new String(uu);
            result.add(q);
            return;
        }
        char current = s[start];// a、b、c,对a来说要进入一次回溯;
        List<Character> x = hashtable.get(current);
        for (int i = 0; i < x.size(); i++) {
            uu.append(x.get(i));
            dfs(s, len, result, hashtable, start + 1, uu);
            uu.deleteCharAt(uu.length() - 1);

        }
    }
}
括号生成

力扣链接

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> result = new ArrayList<>();
        if (n == 0) return result;
        dfs(result, "", 0, 0, n);
        return result;
    }

    private void dfs(List<String> result, String current, int open, int close, int max) {
        if (current.length() == max * 2) {
            result.add(current);
            return;
        }
//如果左括号的数目还小于总数,则
        if (open < max) {
            dfs(result, current + "(", open + 1, close, max);
        }

        if (close < open) {
            dfs(result, current + ")", open, close + 1, max);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Beiwen_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值