代码随想录算法训练营| 77. 组合 、 216.组合总和III 、 17.电话号码的字母组合

77. 组合 

题目

参考文章

思路:这道题目用到了回溯的算法,因此回溯三部曲是有必要的,同时我们要做一个剪枝操作减少递归次数。

1.递归函数的返回值以及参数

2.回溯函数终止条件

3.单层搜索的过程

剪枝操作n - (k - path.size()) + 1 是表示至多起始位置,这样就会减少递归次数。k-path.size()其实就是一个判断当前还需要几位数值才满足题目条件的一个减法。

同时这里是组合问题,元素不重复而且 不同顺序但元素相同也表示同一个组合,所以设置startIndex(控制集合范围的,即1已经在path中,则后续的递归调用中,应该从2开始)是有必要的,这样会减少同一个组合重复出现的情况

k决定了遍历的深度

代码:

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> combine(int n, int k) {
        combineHelper(n, k, 1);
        return result;
    }

    /**
     * 每次从集合中选取元素,可选择的范围随着选择的进行而收缩,调整可选择的范围,就是要靠startIndex
     * @param startIndex 用来记录本层递归的中,集合从哪里开始遍历(集合就是[1,...,n] )。
     */
    private void combineHelper(int n, int k, int startIndex){
        //终止条件
        if (path.size() == k){
            result.add(new ArrayList<>(path));
            return;
        }
        //n - (k - path.size()) + 1是至多的一个起始位置
        for (int i = startIndex; i <= n - (k - path.size()) + 1; i++){
            path.add(i);
            combineHelper(n, k, i + 1);
            path.removeLast();
        }
    }
}

216.组合总和III

题目

参考文章

思路:其实这题和上面组合这一题相似,知识把组合中的数相加,从而进行判断,然后进行回溯的一个过程。这里涉及一个总和 sum,以及存储路径path,和结果集result。当sum大于要的的总和大小时,直接返回,当当前路径大小等于k时,表示已经是满足k个元素的组合,而且sum==targetSum,就直接加到结果集只能,如果sum!=targetSum就直接返回即可。后面就是回溯的过程,这里的回溯过程多了一步,就是sum+=i,和递归结束后的sum-=i 这两个操作,是因为sum+=i要用于后续判断,sum-=i是因为递归完成后,要变回之前的值,方便下次递归

剪枝:9-(k-path.size())+1

这里剪枝是因为,你只能有k个数相加,所以每当你加到一定数值时,当前数值后面的元素已经不足够k个数相加了,所以要进行剪枝,k-path.size()表示就是至少还需要多少个元素,例如k=2,当k-path.size()=2时,表示还需要两个元素,所以这个循环只能遍历到8,因为只有8和9 才是两个元素,遍历到9时,只有9一个元素,已经不满足条件了。同理,k-path.size()=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
        //这里剪枝是因为,你只能有k个数相加,所以每当你加到一定数值时,当前数值后面的元素已经不足够k个数相加了,所以要进行剪枝,k-path.size()表示就是至少还需要多少个元素,例如k=2,当k-path.size()=2时,表示还需要两个元素,所以这个循环只能遍历到8,因为只有8和9 才是两个元素,遍历到9时,只有9一个元素,已经不满足条件了。同理,k-path.size()=1,也是一样的道理,此时只需要一个元素,所以可以遍历到9
		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.电话号码的字母组合


题目

参考文章

思路:这到题目其实就是在digits寻找对应数字,从而在numString中找到对应的字符串,然后进行层层拼接,当传到下一层递归时,num要+1,因为num是用于判断你当前遍历到digits中哪个数字了,递归返回时,把temp中的最后一个字符删除。这样就完成了整个回溯的过程。遍历完成得到的list就是最后结果

代码:

class Solution {
   //设置全局列表存储最后的结果
    List<String> list = new ArrayList<>();

    public List<String> letterCombinations(String digits) {
        if (digits == null || digits.length() == 0) {
            return list;
        }
        //初始对应所有的数字,为了直接对应2-9,新增了两个无效的字符串""
        String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        //迭代处理
        backTracking(digits, numString, 0);
        return list;

    }

    //使用StringBuilder对字符串进行拼接
    StringBuilder temp = new StringBuilder();

    //比如digits如果为"23",num 为0,则str表示2对应的 abc
    public void backTracking(String digits, String[] numString, int num) {
        //遍历全部一次记录一次得到的字符串
        if (num == digits.length()) {//当num的大小等于digits大小时,表示已经到达最后一层,就直接把上一次(即上一层递归)存储的temp值放到结果集中
            list.add(temp.toString());
            return;
        }
        //str 表示当前num对应的字符串
        String str = numString[digits.charAt(num) - '0'];
        for (int i = 0; i < str.length(); i++) {
            temp.append(str.charAt(i));
            
            backTracking(digits, numString, num + 1);
            
            temp.deleteCharAt(temp.length() - 1);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值