leetcode刷题之回溯算法

前言

由于之前的一道回溯算法题目没做出来,最近一段时间专门练习回溯算法的题目,程序写的多了就有感觉知道哪里有问题,算法练习还是集中的专项练习为好。

组合总和

题目一描述

给定一个不重复元素的数组和一个目标值target,找出数组中能够组合成目标值的子集合,注意数组元素可以重复使用。
例如:candidates = [2,3,6,7] target = 7
返回值:
[
[7],
[2,2,3]
]

题目传送

题目一分析

构建一颗树来表示,以target为根节点以数组元素为边做减法操作,叶子节点为0的一条路径为有效路径,去掉有效路径中重复的路径,对于叶子节点为负数的节点表示无效的路径。
构建的树示意图

代码如下
public List<List<Integer>> combinationSum(int[] candidates, int target) {
		List<List<Integer>> res = new ArrayList<List<Integer>>();
		List<Integer> temp = new ArrayList<Integer>();
		if (candidates.length == 0)
			return res;
		dfs(candidates, 0, target, res, temp);
		return res;
	}
	//深度优先,传入对应的数组,当前开始的位置,目标值,以及结果容器和当前路径
	public void dfs(int[] candidates, int start, int target, List<List<Integer>> res, List<Integer> path) {
		//小于0表示不可到达的路径
		if (target < 0)
			return;
		//等于0表示有效路径
		if (target == 0) {
			res.add(new ArrayList(path));
			return;
		}
		//回溯算法核心
		for (int i = start; i < candidates.length; i++) {
			path.add(candidates[i]);
			//当前传递的i表示可以重复的使用改值
			dfs(candidates, i, target - candidates[i], res, path);
			//剪枝操作,回溯到前一个位置
			path.remove(path.size() - 1);
		}
	}

求指定长度的子集

题目二描述

给定两个整数n和k求,返回1…n中所有可能k个数的组合。

输入 n=4,k=2
返回:
[
[1,2],
[1,3],
[1,4],
[2,3],
[2,4],
[3,4]
]

题目传送

题目二分析

每次从数组中选择一个数加入路径中,下一个元素只能够从后面的那些元素中选择(需要变更i的位置,与上面的不同,上面的元素可以重复),选中k个元素的时候停止。如下面的图所示:
示意图

代码如下
public static List<List<Integer>> getCombine(int n, int k) {
		List<List<Integer>> res = new ArrayList<List<Integer>>();
		List<Integer> temp = new ArrayList<Integer>();
		dfsNum(res, temp, k, n, 1);
		return res;
	}

	public static void dfsNum(List<List<Integer>> res, List<Integer> path, int k, int n, int start) {
	//递归回溯的边界,当前的路径大于k个元素,返回
		if (path.size() > k)
			return;
	//当前的元素个数等于k,添加到总的容器中
		if (path.size() == k) {
			res.add(new ArrayList(path));
			return;
		}
	//回溯核心,这个元素只会出现1次(不可能出现k个1或者k个2这样的组合)
		for (int i = start; i <= n; i++) {
			path.add(i);
			dfsNum(res, path, k, n, i + 1);
			path.remove(path.size() - 1);
		}

	}

求子集合

题目三描述

给定一个数组,要求给出数组的所有子集。该题目是从同学博客中看到的,他说自己面试了阿里,好吧,我就相信了,其实有点酸,我没有收到阿里爸爸的面试邀请,好气呦。
题目的博客

题目三分析

其实和上一个题目比较类似,就是构建一棵树,进行选择,只是这次是找到所有的子集,不是一个定长的集合了,上面相当于多一次的筛选。图我就不画了,我也想要阿里的面试。

代码如下
public static List<List<Integer>> getSub(int[] num) {
		List<List<Integer>> res = new ArrayList<List<Integer>>();
		List<Integer> temp = new ArrayList<Integer>();
		dfsSub(res, temp, num, 0);
		return res;
	}
	//回溯算法
	public static void dfsSub(List<List<Integer>> res, List<Integer> path, int[] num, int start) {
	//当前的元素已经越界了,返回
		if (start > num.length)
			return;
		//只要不是空路径就加入总的集合中
		if (path.size() > 0)
			res.add(new ArrayList<>(path));
		//回溯的核心,多理解几次,多写几次就好了
		for (int i = start; i < num.length; i++) {
			path.add(num[i]);
			dfsSub(res, path, num, i + 1);
			path.remove(path.size() - 1);
		}
	}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值