4.28 电话号码组合 && 组合总和(回溯)

​​​​​​电话号码的字母组合

原理思想:回溯不需要剪枝,注意回溯结束的条件和恢复现场。由于这道题是求组合的种类,所以需要恢复现场寻找不同的组合。

public List<String> solution(String digits) {
		List<String> res = new LinkedList<>();
		int length = digits.length();
		//判断空值,LeetCode里面的测试用例总有空值
		if(length==0)return res;
		//hashmap存储电话的对应值
		Map<Character, String> map = new HashMap<>();
		map.put('2', "abc");
        map.put('3', "def");
        map.put('4', "ghi");
        map.put('5', "jkl");
        map.put('6', "mno");
        map.put('7', "pqrs");
        map.put('8', "tuv");
        map.put('9', "wxyz");
        //StringBuilder是为了找寻每一种可能的结果,其允许对字符串进行多次修改
        backtrack(res, map, digits, length, 0, new StringBuilder());
        return res;
	}
	
	public void backtrack(List<String> res, Map<Character, String> map, String digits, int length, int t, StringBuilder result) {
		if(t==length) { //注意回溯结束条件
			res.add(result.toString());
		}
		else {
			Character a = digits.charAt(t);
			String letter = map.get(a);
			int letterLen = letter.length();
			for(int i=0; i<letterLen; i++) {
				result.append(letter.charAt(i));
				backtrack(res, map, digits, length, t+1, result);
				//恢复现场,因为要求的是不同的组合,回溯的时候需要去掉下层的结果,也就是result后面的字符
//不然结果是这样[ad, ade, adef, adefbd, adefbde, adefbdef, adefbdefcd, adefbdefcde, adefbdefcdef]
				//注意参数是t,不是i,思想:恢复的是最后一个操作的变量
				result.deleteCharAt(t);
			}
		}
	}

组合总和

去重思想:搜索时去重,遇到这一类相同元素不计算顺序的问题,我们在搜索的时候就需要 按某种顺序搜索。具体的做法是:每一次搜索的时候设置 下一轮搜索的起点 begin,如下图:

即:从每一层的第 2 个结点开始,都不能再搜索产生同一层结点已经使用过的 candidate 里的元素。 

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
		Arrays.sort(candidates);
		List<List<Integer>> res = new LinkedList<>();
		int length = candidates.length;
		if(length==0)return res; //去除特殊情况
		backtrack(res, candidates, new LinkedList<Integer>(), target, 0, 0);
		return res;
		
	}
	
	public void backtrack(List<List<Integer>> res, int [] candidates, List<Integer> result, int target, int begin, int sum) {
		if (sum==target) {  
			res.add(new LinkedList<>(result)); //这里需要重新new一个list变量,因为list是动态变化的,如果使用原来的result变量,结果res也会因此改变
			return;
		}
		else {
			for(int i=begin; i<candidates.length; i++) {
				if(sum+candidates[i]>target)break; //约束条件进行减枝
				else {
					result.add(candidates[i]);
					//由于每一个元素可以重复使用,下一轮搜索的起点依然是 i,这里非常容易弄错,这里的i并不是数的层数所以不需要+1
					//因为可以重复使用,所以层数是不确定的,不能用层数t来当做回溯结束的条件
					backtrack(res, candidates, result, target, i, sum+candidates[i]);
				}
				result.remove(result.size()-1);//恢复现场,删除的是最后一个进入list的元素
			}
		}
	}
}

更简洁的backtrack:

public void backtrack(List<List<Integer>> res, int [] candidates, List<Integer> result, int target, int begin, int sum) {
		if (sum==target) {  
			res.add(new LinkedList<>(result)); 
			return;
		}
		else {
			for(int i=begin; i<candidates.length; i++) {
				if(sum+candidates[i]<=target){
					result.add(candidates[i]);
					backtrack(res, candidates, result, target, i, sum+candidates[i]);
					result.remove(result.size()-1);
				}
			}
		}
	}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值