代码随想录第25天 | 回溯算法part02

代码随想录算法训练营第25天 | 回溯算法part02 216.组合总和III 17.电话号码的字母组合

题目一 组合总和3

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:

  • 所有数字都是正整数。
  • 解集不能包含重复的组合。
    示例 1: 输入: k = 3, n = 7 输出: [[1,2,4]]

关键:本题就是在[1,2,3,4,5,6,7,8,9]这个集合中找到和为n的k个数的组合。
其余思路和上一题相同。
本题k相当于树的深度,9(因为整个集合就是9个数)就是树的宽度。

  • target目标和,也就是题目中的n。
  • k就是题目中要求k个数的集合。
  • sum为已经收集的元素的总和,也就是path里元素的总和。
  • startIndex为下一层for循环搜索的起始位置。注意startindex所处的集合是1~9数字的集合,因此下标从1开始,到9结束。
public List<List<Integer>> ans = new ArrayList<>();
public LinkedList<Integer> path = new LinkedList<>();

public List<List<Integer>> combinationSum3(int k, int n) 
{
	recursion(n, k, 0, 1);//sum = 0
	return ans;
}
//k为深度,9为长度
public void recursion(int target, int k, int sum, int startindex)//参数不着急定
{
	//end
	if(path.size() == k)//限制深度
	{
		if(sum == target)
		{
			ans.add(new ArrayList<>(path) );
			return;
		}
	}
	//every
	for(int i = startindex; i <= 9; i++)//[1,2,3,4,5,6,7,8,9]
	{
		sum += i;
		path.add(i);//add this
		recursion(target,k, sum,i+1);
		sum -= i;
		path.removeLast();//delete this
	}
}

sum为统计已有数字加起来的总和,会随着每次迭代更新,因此加入形参中。其实这里sum这个参数也可以省略,
每次target减去选取的元素数值,然后判断如果target为0了,说明收集到符合条件的结果了。
target为总和应该达到的目标,可使用全局变量,不用每次都传入。

同理,剪枝操作也很容易想到。本题剪枝的因素有两个。
已选元素总和如果已经大于n(图中数值为4)了,那么往后遍历就没有意义了,直接剪掉。

同样,for循环的范围也可以剪枝,i <= 9 - (k - path.size()) + 1就可以了。

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;
	}
}

题目二 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与九键输入法按键相同)。注意 1 不对应任何字母。

理解本题后,要解决如下三个问题:

  1. 数字和字母如何映射
  2. 两个字母就两个for循环,三个字符就三个for循环,以此类推,然后发现代码根本写不出来
  3. 输入1 * #按键等等异常情况

与之前题目不同的是,本题数字和字母应当使用map或者二维数组进行映射。
横向为输入数字的顺序,纵向为选择字符的顺序。

下面用一个字符串数组allletter[]保存所有映射,以数组下标为数字,存储值为对应的字符串。
从这个数组中取的时候注意考虑字符串和它对应的数字的转换,根据不同语言的特性选择相应的转换函数。

注意考虑输入为空的情况,应该返回一个空列表[] 而不是[“”] .

index是用于遍历给定字符串digits的,因此下标从0开始,调用函数传参开始也是0.
如果index与digit长度相等,则证明遍历完毕,将一个字符串结果加入答案中。


public List<String> ans = new ArrayList<>();
public StringBuilder path = new StringBuilder();

private String[] allletter = {//0, 1, 2, 3, 4, 5, 6, 7, 8, 9
	"" ,"" ,"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"
};

public List<String> letterCombinations(String digits) 
{
	if(digits == null || digits.length() == 0)
		return ans;
	recursion(digits, allletter, 0);
	return ans;
}
public void recursion(String digits, String[] allletter, int index)
{
	//end
	if(index == digits.length() )
	{
		ans.add(path.toString() );
		return;
	}
	int digit = digits.charAt(index) - '0';//letter to num
	String letter = allletter[digit];
	//every
	for(int i=0; i<letter.length(); i++)
	{
		path.append( letter.charAt(i) );//加入字符
		recursion(digits,allletter, index+1);
		path.deleteCharAt(path.length() - 1);//回溯
	}

}

注意这里for循环,可不像是在 回溯算法:求组合问题 和 回溯算法:求组合总和 中,从startIndex开始遍历的。因为本题每一个数字代表的是不同集合,也就是求不同集合之间的组合。

  • 时间复杂度: O( 3 m ∗ 4 n 3^m * 4^n 3m4n),其中 m 是对应四个字母的数字个数,n 是对应三个字母的数字个数
  • 空间复杂度: O( 3 m ∗ 4 n 3^m * 4^n 3m4n)
  • 14
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值