backtracking Leetcode 回溯算法题

77.组合

第一个位置选择有 n 种,接下来每个位置只能在前面选择数字的后面选,所以有了 beg 参数,才能保持不重复

剪枝:res.size + (n - beg + 1) < k , 已有答案的长度 + 剩余所有未选择的个数 都小于最终答案长度了 就没有必要尝试下去

class Solution {
    
    List<List<Integer>> ans = new ArrayList<List<Integer>>();

    public List<List<Integer>> combine(int n, int k) {
        List<Integer> res = new ArrayList<Integer>();
        dfs(res, 1, n, k);
        return ans;
    }

    public void dfs(List<Integer> res, int beg, int n, int k){
        if(k == 0){
            // 这里一定要 new 一个新的 ArrayList 啊,否则最后加进去的 res 都是 null
            ans.add(new ArrayList<Integer>(res));
            return;
        }
        if(res.size() + n - beg + 1 < k) return;

        for(int i = beg; i <= n; ++ i){
            res.add(i);
            dfs(res, i + 1, n, k - 1);
            res.remove(res.size() - 1);
        }
    }
}

216.组合总和III

class Solution {
    
    List<Integer> res = new ArrayList<>();
    List<List<Integer>> ans = new ArrayList<List<Integer>>();

    public List<List<Integer>> combinationSum3(int k, int n) {
        dfs(1, k, n);
        return ans;
    }

    public void dfs(int beg, int k, int n){
        if(k < 0 || n < 0){
            return;
        }

        // 剩下最大的 k 个数相加都小于 n || 最小的 k 个数相加都大于 n : 也就没有必要继续遍历了
        if(k*(19-k)/2 < n || k*(2*beg+k-1)/2 > n){
            return;
        }

        if(k == 0 && n == 0){
            ans.add(new ArrayList(res));
            return;
        }

        for(int i = beg; i <= 9; ++ i){
            res.add(i);
            dfs(i + 1, k - 1, n - i);
            res.remove(res.size() - 1);
        }
    }
}

随想录代码:

class Solution {
	List<List<Integer>> result = new ArrayList<>();
	LinkedList<Integer> path = new LinkedList<>();

	public List<List<Integer>> combinationSum3(int k, int n) {
		backTracking(n, k, 1, 0);
		return result;
	}

	private void backTracking(int targetSum, int k, int startIndex, int sum) {
		// 减枝
		if (sum > targetSum) {
			return;
		}

		if (path.size() == k) {
			if (sum == targetSum) result.add(new ArrayList<>(path));
			return;
		}

		// 减枝 9 - (k - path.size()) + 1
		for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) {
			path.add(i);
			sum += i;
			backTracking(targetSum, k, i + 1, sum);
			//回溯
			path.removeLast();
			//回溯
			sum -= i;
		}
	}
}

17.电话号码的字母组合

第一次遍历判断第一个位置的 beg 所对应的字符 idx,遍历所有字符的可能

第二次遍历判断第二个位置的 beg + 1 所对应的字符 idx,遍历所有字符的可能

一直到所有位置都被遍历完,也就是 digits 所有位置都被遍历完,那么 beg 就等于 digits.length() 了,此时记录答案

注意,digits 的第 beg 位置,对应的数字是 idx,该 idx 对应的字符才是要遍历的字符

  • 字符串中提取对应位置的字符:digits.chatAt(beg)
  • 字符 char 转为 int 类型:
    • 首先 char 转为 String :String.valueOf(x)
    • String 转为 int : Integer.parseInt(xx)
class Solution {
    List<String> table = Arrays.asList("", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz");
    List<String> ans = new ArrayList<>();
    public List<String> letterCombinations(String digits) {
        if(digits.equals("")) return ans;
        dfs(digits, new StringBuilder(), 0);
        return ans;
    }

    public void dfs(String digits, StringBuilder res, int beg){
        if(beg == digits.length()){
            ans.add(res.toString());
            return;
        }
        int idx = Integer.parseInt(String.valueOf(digits.charAt(beg)));
        for(int j = 0; j < table.get(idx).length(); ++j){
            res.append(table.get(idx).charAt(j));
            dfs(digits, res, beg + 1);
            res.delete(beg, beg + 1);
        }
    }
}

39.组合总和

不限制元素使用次数,使其达到目标数,的所有不同组合

  • 由于可以重复选取,所以 i 没有+1
  • 由于结果要求组合不同,所以不能选 i 之前的数,这里用for循环让 i 只能往后选择剩余的数
class Solution {
    List<Integer> res = new ArrayList<>();
    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        dfs(res, candidates, target, 0);
        return ans;
    }

    public void dfs(List<Integer> res, int[] candidates, int target, int beg){
        
        if(target < 0){
            return;
        }
        if(target == 0){
            ans.add(new ArrayList(res));
            return;
        }

        for(int i = beg; i < candidates.length; ++ i){
            res.add(candidates[i]);
            dfs(res, candidates, target - candidates[i], i);
            res.remove(res.size() - 1);
        }
    }
}

40.组合总和II

元素有重复,选出总和达到目标数的不同组合

由于元素有重复,所以一个数只能连续的被使用,那么就需要对原数组排序

我这里使用了一个freq记录每个数的可使用次数,当这个数的使用次数被用完,或者以后都不打算使用这个数的时候,才可以用下一个数

class Solution {
    List<int[]> freq = new ArrayList<>();
    List<Integer> res = new ArrayList<>();
    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        int num = 1, size = candidates.length;
        for(int i = 0; i < size; ++ i){
            if(i < size -1 && candidates[i] == candidates[i+1]){
                ++num;
            }else{
                freq.add(new int[]{candidates[i], num});
                num = 1;
            }
        }
        dfs(target, 0);
        return ans;
    }

    public void dfs(int target, int beg){
        if(target < 0){
            return;
        }
        if(target == 0){
            ans.add(new ArrayList<>(res));
        }

        for(int i = beg; i < freq.size(); ++i){
            res.add(freq.get(i)[0]);
            --freq.get(i)[1];
            dfs(target-freq.get(i)[0], freq.get(i)[1] == 0 ? i+1 : i);
            res.remove(res.size() - 1);
            ++freq.get(i)[1];
        }
    }
}

更优雅

另一个方法是让当前搜索层级里不出现相同的元素,要想同一个值使用多次,则必须在dfs的下一个层级

class Solution {
    List<Integer> res = new ArrayList<>();
    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        dfs(candidates, target, 0);
        return ans;
    }

    public void dfs(int[] candidates, int target, int beg){
        if(target < 0){
            return;
        }
        if(target == 0){
            ans.add(new ArrayList<>(res));
        }

        for(int i = beg; i < candidates.length; ++i){
            if(i > beg && candidates[i] == candidates[i-1]){
                continue;
            }
            res.add(candidates[i]);
            dfs(candidates, target-candidates[i], i+1);
            res.remove(res.size() - 1);
        }
    }
}

131.分割回文串

动态规划

动态规划判断一个字串是否回文:

f[i][j] = f[i+1][j-1] && (s.charAt(i) == s.charAt(j));

注意,先定后边界 j,再遍历所有前边界 i

dfs 遍历所有可能划分

class Solution {
    boolean[][] f;
    List<String> seq = new ArrayList<>();
    List<List<String>> res = new ArrayList<List<String>>();
    public List<List<String>> partition(String s) {
        int n = s.length();
        f = new boolean[n][n];
        for(int i = 0; i < n; ++ i){
            for(int j = 0; j <= i; ++ j){
                f[i][j] = true;
            }
        }

        for(int j = 1; j < n; ++ j){
            for(int i = 0; i < j; ++ i){
                f[i][j] = f[i+1][j-1] && (s.charAt(i) == s.charAt(j));
            }
        }

        dfs(s, 0);
        return res;
    }

    public void dfs(String s, int beg){
        if(beg == s.length()){
            res.add(new ArrayList<String>(seq));
            return;
        }
        for(int j = beg; j < s.length(); ++ j){
            if(f[beg][j]){
                seq.add(s.substring(beg, j + 1));
                dfs(s, j + 1);
                seq.remove(seq.size() - 1);
            }
        }
    }
}

记忆化搜索

将回文串的判断用dfs实现:

class Solution {
    int[][] f;
    List<String> seq = new ArrayList<>();
    List<List<String>> res = new ArrayList<List<String>>();
    public List<List<String>> partition(String s) {
        int n = s.length();
        f = new int[n][n];
        dfs(s, 0);
        return res;
    }

    public void dfs(String s, int beg){
        if(beg == s.length()){
            res.add(new ArrayList<String>(seq));
            return;
        }
        for(int j = beg; j < s.length(); ++ j){
            if(isHuiwen(s, beg, j) == 1){
                seq.add(s.substring(beg, j + 1));
                dfs(s, j + 1);
                seq.remove(seq.size() - 1);
            }
        }
    }
    public int isHuiwen(String s, int i, int j){
        if(f[i][j] != 0){
            return f[i][j];
        }
        if(i >= j){
            f[i][j] = 1;
        }else if(s.charAt(i) == s.charAt(j)){
            f[i][j] = isHuiwen(s, i+1, j-1);
        }else{
            f[i][j] = -1;
        }
        return f[i][j];
    }
}

78.子集

  • 迭代法:可以枚举二进制
  • 回溯法:
class Solution {
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    List<Integer> ans = new ArrayList<>();
    public List<List<Integer>> subsets(int[] nums) {
        dfs(nums, 0);
        return res;
    }

    public void dfs(int[] nums, int beg){
        res.add(new ArrayList(ans));
        if(beg == nums.length){
            return;
        }

        for(int i = beg; i < nums.length; ++ i){
            ans.add(nums[i]);
            dfs(nums, i + 1);
            ans.remove(ans.size()-1);
        }
    }
}

90.子集II

与之前的 40.组合总和II 解法相同,但没有达到目标数的要求,更简单。

class Solution {
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    List<Integer> ans = new ArrayList<>();
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        dfs(nums, 0);
        return res;
    }

    public void dfs(int[] nums, int beg){
        res.add(new ArrayList(ans));
        for(int i = beg; i < nums.length; ++ i){
            if(i > beg && (nums[i-1] == nums[i])){
                continue;
            }
            ans.add(nums[i]);
            dfs(nums, i + 1);
            ans.remove(ans.size() - 1);
        }
    }
}

491.递增子序列

由于元素有重复且需要维护相对顺序,所以无法通过排序去重

使用 HashSet 对同一层的数据去重

class Solution {
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    List<Integer> ans = new ArrayList<>();
    public List<List<Integer>> findSubsequences(int[] nums) {
        dfs(nums, 0, -101);
        return res;
    }

    public void dfs(int[] nums, int beg, int pre){
        if(ans.size() > 1){
            res.add(new ArrayList(ans));
        }
        Set<Integer> used = new HashSet<>();
        for(int i = beg; i < nums.length; ++ i){
            if(nums[i] < pre || used.contains(nums[i])) {
                continue;
            }
            used.add(nums[i]);
            ans.add(nums[i]);
            dfs(nums, i + 1, nums[i]);
            ans.remove(ans.size() - 1);
        }
    }
}

46.全排列

将一个无重复元素的数组全排列

used 记录已经使用过的元素

每次都遍历所有,只往下 dfs 未使用的元素

class Solution {
    boolean[] used;
    int n;
    List<Integer> ans = new ArrayList<>();
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    public List<List<Integer>> permute(int[] nums) {
        n = nums.length;
        used = new boolean[n];
        dfs(nums);
        return res;
    }

    public void dfs(int[] nums){
        if(ans.size() == n){
            res.add(new ArrayList(ans));
            return;
        }
        for(int i = 0; i < n; ++ i){
            if(used[i] == false){
                ans.add(nums[i]);
                used[i] = true;
                dfs(nums);
                used[i] = false;
                ans.removeLast();
            }
        }
    }
}

47.全排列II

有重复元素的全排列

求全排列的方法加上去重算法

由于顺序无所谓,所以可以先排序,

对于同一层的搜索:让上一次搜索的元素与下一次搜索的元素不同

记录上一次用到的元素,判断当前元素是否与前一个元素相同

class Solution {
    boolean[] used;
    int n;
    List<Integer> ans = new ArrayList<>();
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    public List<List<Integer>> permuteUnique(int[] nums) {
        n = nums.length;
        used = new boolean[n];
        Arrays.sort(nums);
        dfs(nums);
        return res;
    }

    public void dfs(int[] nums){
        if(ans.size() == n){
            res.add(new ArrayList(ans));
            return;
        }
        int pre = 100;
        for(int i = 0; i < n; ++ i){
            if(used[i] == false && nums[i] != pre){
                ans.add(nums[i]);
                used[i] = true;
                pre = nums[i];
                dfs(nums);
                used[i] = false;
                ans.removeLast();
            }
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/554013.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IO引脚服用和映射

什么是端口复用 STM32F4 有很多的内置外设&#xff0c;这些外设的外部引脚都是与 GPIO 复用的。也就是说&#xff0c;一个 GPIO如果可以复用为内置外设的功能引脚&#xff0c;那么当这个 GPIO 作为内置外设使用的时候&#xff0c;就叫做复用。在芯片数据手册或STM32F4XX参考手…

传感器融合 | 适用于自动驾驶场景的激光雷达传感器融合项目_将激光雷达的高分辨率成像+测量物体速度的能力相结合

项目应用场景 面向自动驾驶场景的激光雷达传感器融合&#xff0c;将激光雷达的高分辨率成像测量物体速度的能力相结合&#xff0c;项目是一个从多个传感器获取数据并将其组合起来的过程&#xff0c;可以更加好地进行环境感知。项目支持 ubuntu、mac 和 windows 平台。 项目效果…

ASP.NET基于TCP协议的简单即时通信软件的设计与实现

摘 要 即时通信(Instant Message)&#xff0c;由于其具有实时性、跨平台性、成本低、效率高等优点而受到广泛的使用。设计并实现一个能够处理多用户进行实时、安全的即时通信系统具有较强的现实意义。即时通信的底层通信是通过SOCKET套接字接口实现的。当前的主流UNIX系统和微…

Android --- Activity

官方文档-activity Activity 提供窗口&#xff0c;供应在其中多个界面。此窗口通常会填满屏幕&#xff0c;但也可能小于屏幕并浮动在其他窗口之上。 大多数应用包含多个屏幕&#xff0c;这意味着它们包含多个 Activity。通常&#xff0c;应用中的一个 Activity 会被指定主 Ac…

Linux数据库自动备份 - 定时任务发到百度云盘、坚果云、邮箱附件

前言 1. 坚果云的webdav云盘最好&#xff01; &#xff08;免费账号每月1G上传流量&#xff09; 2. 不建议数据库备份文件发送到SMTP邮箱&#xff0c;因为对方服务器非常容易当做垃圾邮件处理&#xff0c;而且发信的SMTP账号会被封禁&#xff08;实测163发到QQ邮箱被封&…

lambda捕获列表

lambda是C11新特性之一&#xff0c;优点是&#xff1a; 1.可以直接匿名定义目标函数或函数对象&#xff0c;不需要额外写一个函数 2.lambda是一个匿名的内联函数 捕获列表 总结&#xff1a;【】为值捕获&#xff0c;只读 【&】为引用捕获&#xff0c;可读可写

Midjourney指南 - 生成高分辨率图片(内容已更新至V5)

Midjourney 首先为每个作业生成一个低分辨率图片网格(2x2)。你可以在选择其中任一图片&#xff0c;使用 Midjourney upscaler 来增加尺寸并添加更多细节。有多种可用于放大图像的放大模型。 每个图像网格下方的按钮用于放大所选图像。U1 U2 U3 U4 注&#xff1a;upscaler 以下…

震惊金融界!巴克莱银行报告称去年投资诈骗激增29%

巴克莱银行 (Barclays) 发布的令人担忧的数据显示&#xff0c;在过去一年里&#xff0c;投资诈骗数量激增了 29%&#xff0c;震惊了金融界。这些诈骗给该银行的活期账户客户造成了巨大损失&#xff0c;占欺诈者损失资金的最高比例&#xff0c;平均索赔超过14,000英镑。 投资骗…

如何合理利用多个中国大陆小带宽服务器?

我们知道在中国大陆带宽单价非常昂贵&#xff0c;一个1Mbps 带宽的机子一年就得卖好几百人民币&#xff0c;这是不值当的&#xff0c;当然我们可以去低价漂阿里云、腾讯云的轻量服务器&#xff0c;99包年&#xff0c;但是带宽太小很难崩。 所以&#xff0c;我们必须构建一个能够…

怎么购买GPT api

怎么购买GPT api GPT API是由OpenAI提供的一种应用程序编程接口&#xff08;API&#xff09;&#xff0c;允许开发者通过编程方式访问OpenAI开发的GPT&#xff08;Generative Pre-trained Transformer&#xff09;模型。GPT是一种基于深度学习的自然语言处理技术&#xff0c;主…

刷题之Leetcode19题(超级详细)

19.删除链表的倒数第N个节点 力扣题目链接(opens new window)https://leetcode.cn/problems/remove-nth-node-from-end-of-list/ 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 进阶&#xff1a;你能尝试使用一趟扫描实现吗&#x…

爱普生计时设备AUTOMOTIVE RA8900CE DTCXO RTC

主要特点出场已校准带有DTCXO的RTC&#xff0c;并且内部集成晶体单元高精度: 3.4 ppm 40 to 85 C(9 s/月.)时钟输出:1 Hz.1024 Hz.32.768 kHzI 2 C Interface: Fast mode (400 kHz)The l2C-Bus is a trademark ofNXP Semiconductors供电电压: 2.5-5.5 V(main),1.6-5.5 V(备份电…

软考132-上午题-【软件工程】-沟通路径

一、定义 1-1、沟通路径1 沟通路径 1-2、沟通路径2 沟通路径 n-1 二、真题 真题1&#xff1a; 真题2&#xff1a; 真题3&#xff1a;

国外AI programmer 后来者SWE-agent,Devin不在孤寂

如果你正在寻找一种人工智能(AI)自主软件工程师Devin的替代品,它的强大程度足以与最近宣布的自主AI编码平台竞争。这位新手就是SWE-Agent!它是由普林斯顿大学NLP小组创造的开源人工智能程序员,旨在自主解决GitHub问题并实现最先进的性能,估值目标为20亿美元。SWE Agent在S…

jar包混淆

由于开发需要&#xff0c;不让甲方反编译出源代码。 命令如下&#xff1a; java -jar classfinal-fatjar-1.2.1.jar -file mis-admin.jar -libjars mis-ducg-3.5.0.jar -packages com.mis,cn.edu -pwd 123456 -Y 反编译软件编译的源码如下&#xff1a;直接null&#xff0c;成…

k8s使用harbor私有仓库镜像 —— 筑梦之路

官方文档: Secret | Kubernetes ImagePullSecrets的设置是kubernetes机制的另一亮点&#xff0c;习惯于直接使用Docker Pull来拉取公共镜像&#xff0c;但非所有容器镜像都是公开的。此外&#xff0c;并不是所有的镜像仓库都允许匿名拉取&#xff0c;也就是说需要身份认证&…

回归预测 | Matlab实现GWO-GPR灰狼算法优化高斯过程回归多变量回归预测

回归预测 | Matlab实现GWO-GPR灰狼算法优化高斯过程回归多变量回归预测 目录 回归预测 | Matlab实现GWO-GPR灰狼算法优化高斯过程回归多变量回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab实现GWO-GPR灰狼算法优化高斯过程回归多变量回归预测 1.Matlab实现…

Windows如何下载Bun并在前端Vue或React项目上替代Yarn或Npm

Bun Bun网站 Bun 在 Windows 上下载并安装 Bun 非常简单。你可以使用以下命令在 Windows 10 或更高版本上安装 Bun powershell -c "irm bun.sh/install.ps1 | iex"“powershell”不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件 PowerShell 命令解决…

JET毛选学习笔记:如何利用《实践论》学习实验

一、个人背景介绍 本人本科读的是预防医学专业&#xff08;因为没考上临床&#xff09;&#xff0c;硕博连读&#xff08;报名人少&#xff0c;我报了就得了&#xff09;的时候专业是流行病与卫生统计学&#xff0c;除了学习流行病学、统计学&#xff08;忘得差不多了&#xf…

Linux 操作系统指令和Vscdoe安装

1、Linux系统介绍 Linux系统的背景介绍我就不介绍了&#xff0c;有兴趣的可以去看看其发展史。 1.1 Linux操作系统的主要特点 Linux操作系统的重要思想&#xff1a;一切皆文件 Linux操作系统的特性&#xff1a; 完全免费 支持多平台 支持多用户、多任务 有良好的界面 完美兼容…