力扣算法 77. 组合 216.组合总和III 17.电话号码的字母组合

学习内容

力扣算法

77.组合

xxxxxx

具体内容

77.组合

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按任何顺序返回答案。

示例 1:

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

示例 2:

输入:n = 1, k = 1 输出:[[1]]

题目链接

做题思路

求组合是不包括重复的,而回溯一般用二叉树,如下图所示
在这里插入图片描述

解题

class Solution {
	//存放结果集的数组
	List<List<Integer>> result = new ArrayList<>();
	//存放满足条件的结果
	LinkedList<Integer> path = new LinkedList<>();
	public List<List<Integer>> combine(int n, int  k) {
		backtracking(n,k,1);
		return result;
	}
	//target表示下一次递归从哪里开始,可以理解为是对横向的处理
	public void backtracking(int n,int k,int target){
		if(path.size() == k){
			result.add(new ArrayList(path));
		}
		for(int i = target;i <= n;i++){
			//处理节点
			path.add(i);
			//递归
			//i是对纵向的处理
			backtracking(n,k,i+1);
			//回溯
			path.removeLast();
		}
	}
}

解答代码存在的疑问

1、为什么是i+1而不是target+1

backtracking(n,k,i+1);

因为target是记录本层递归的集合从哪里开始遍历,抽象的看他是横向的,而i是纵向的,是从某个位置开始,再不断向后遍历
在这里插入图片描述
2.为什么是result.add(new ArrayList(path)),而不是result.add(path);

result.add(new ArrayList(path));

res.add(new ArrayList(path)):开辟一个独立地址,地址中存放的内容为path集合,后续path的变化不会影响到res。
res.add(path):将res尾部指向了path地址,后续path内容的变化会导致res的变化。

剪枝优化

当n=4时,k=4时,就会发现只需要经历一次for循环就可以,无需再进行后续的操作,因为当n取2时,后面总的元素已经不够k取值,即题目给出的元素要大于k值才会往下操作

集合仍需要的元素个数:k-path.size()
剩余的元素:n-(k-path.size())
整合可得到:i <=n-(k-path.size())
由于是从1开始的,即后面要加1,右闭区间 i <=n-(k-path.size())+1

class Solution {
	//存放结果集的数组
	List<List<Integer>> result = new ArrayList<>();
	//存放满足条件的结果
	LinkedList<Integer> path = new LinkedList<>();
	public List<List<Integer>> combine(int n, int  k) {
		backtracking(n,k,1);
		return result;
	}
	//target表示下一次递归从哪里开始,可以理解为是对横向的处理
	public void backtracking(int n,int k,int target){
		if(path.size() == k){
			result.add(new ArrayList(path));
		}
		//剪枝优化
		for(int i = target;i <= n-(k-path.size())+1;i++){
			//处理节点
			path.add(i);
			//递归
			//i是对纵向的处理
			backtracking(n,k,i+1);
			//回溯
			path.removeLast();
		}
	}
}

216.组合总和III

找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:

只使用数字1到9
每个数字 最多使用一次 

返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

示例 1:

输入: k = 3, n = 7 输出: [[1,2,4]] 解释: 1 + 2 + 4 = 7 没有其他符合的组合了。

示例 2:

输入: k = 3, n = 9 输出: [[1,2,6], [1,3,5], [2,3,4]] 解释: 1 + 2 + 6 = 9 1 +
3 + 5 = 9 2 + 3 + 4 = 9 没有其他符合的组合了。

示例 3:

输入: k = 4, n = 1 输出: [] 解释: 不存在有效的组合。
在[1,9]范围内使用4个不同的数字,我们可以得到的最小和是1+2+3+4 = 10,因为10 > 1,没有有效的组合。

题目链接

做题思路

该问题与上述问题思路一致,只不过是多了个和为 n的条件,且提供的数也是1-9

解题

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

解答代码存在的疑问

1、return、continue、break区别
(1)return关键字并不是专门用于跳出循环的,return的功能是结束一个方法。 一旦在循环体内执行到一个return语句,return语句将会结束该方法,循环自然也随之结束。与continue和break不同的是,return直接结束整个方法,不管这个return处于多少层循环之内。

(2)break用于完全结束一个循环,跳出循环体。不管是哪种循环,一旦在循环体中遇到break,系统将完全结束循环,开始执行循环之后的代码。

(3)continue的功能和break有点类似,区别是continue只是中止本次循环,接着开始下一次循环。而break则是完全中止循环。

17.电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
在这里插入图片描述

示例:

输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

题目链接

做题思路

(1)首先要创建对应的数组,来表示电话号码所包含的字母
(2)获取题目输入所对应的字符串,获得数字,再获得相对应的字符串
(3)之后再利用回溯的思想,按上述题目的方法解题

解题

class Solution {
	//存放结果的结果集
	List<String> result = new ArrayList<>();
	public List<String> letterCombinations(String digits) {
		if(digits.length() == 0 || digits == null){
			return result;
		}
		//初始对应所有的数字,为了直接对应2-9,新增了两个无效的字符串""
        String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        backtracking(digits,0,numString);
        return result;
	}
//每次迭代获取一个字符串,所以会设计大量的字符串拼接,所以这里选择更为高效的 StringBuild
    StringBuilder temp = new StringBuilder();
	public void backtracking(String digits,int num, String[] numString){
		//终止条件 当num刚好是digits的长度
		if(num == digits.length()){
			result.add(temp.toString());
            return;
		}
		//获得题目相对应字符的位置 
		int nums = digits.charAt(num) - '0';
		//获得对应的字符串
		String str = numString[nums];
		for(int i = 0;i < str.length(); i++) {
            temp.append(str.charAt(i));
            //c
            backtracking(digits, num + 1,numString);
            //剔除末尾的继续尝试
            temp.deleteCharAt(temp.length() - 1);
      }
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值