回溯算法

回溯算法定义

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

784.字母大小写全排列

题目描述

给定一个字符串S,通过将字符串S中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。
示例:
输入: S = “a1b2”
输出: [“a1b2”, “a1B2”, “A1b2”, “A1B2”]

思路分析:

通过深度优先搜索,如果当前下标为index的字符为字母,则改变字母的大小写,并在递归中形成分支。当递归到下标index==S.length()时,将当前的字符串记录到数组res中。

    public List<String> letterCasePermutation(String S) {
        dfs(S, 0);
        return res;
    }
    
    List<String> res = new ArrayList<>();
    private void dfs(String S, int index) {
    	if(index == S.length()) {
    		res.add(S);
    		return;
    	}
    	dfs(S, index+1);
    	
    	StringBuilder temp = new StringBuilder();
    	for(int i=0;i<S.length();i++) {
    		temp.append(S.charAt(i));
    	}
    	char c = temp.charAt(index);
    	if(Character.isAlphabetic(c)) {
    		if(Character.isUpperCase(c))
    			c = Character.toLowerCase(c);
    		else c = Character.toUpperCase(c);
    		temp.replace(index, index+1, c+"");
    		dfs(temp.toString(), index+1);
    	}
    }

78.子集

题目描述

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:[ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]

思路分析

递归地遍历数组,每次进行

  1. 不添加当前元素,将subset直接用于继续递归
  2. 将当前元素添加到subset中,再将subset添加到res中,然后继续递归
class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        res.add(new ArrayList<>(0));
        dfs(nums, new ArrayList<>(), 0);
        return res;
    }
    
    List<List<Integer>> res = new ArrayList<>();
    private void dfs(int[] nums, List<Integer> subset, int index) {
    	if(index==nums.length)
    		return;
    	
    	dfs(nums, subset, index+1);
    	
    	List<Integer> temp = new ArrayList<>();
    	for(int e: subset) {
    		temp.add(e);
    	}
    	temp.add(nums[index]);
    	res.add(temp);
    	dfs(nums, temp, index+1);
    }
}

90. 子集 II

题目描述

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: [1,2,2]
输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]

思路分析

78.子集不同的是,输入的元素中可能包含重复元素。如果不考虑元素重复,那么输出的结果为: [ [1], [2], [2], [1,2,2], [2,2], [1,2], [1,2], [] ]。

观察发现,当原数组中元素连续重复时,产生相同子集的原因是:某一个递归过程中,未将之前的重复元素添加到子集中,而将当前的重复元素添加到子集中。故当nums[i]==nums[i-1]时,维护一个count变量和一个subCount变量分别记录当前位置之前原数组重复元素个数、子集中该元素重复个数。若两变量相等,则可以将当前元素添加到子集中。注意:为了让原数组的重复元素紧邻,需要先对原数组进行排序。

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
    	Arrays.sort(nums);
        res.add(new ArrayList<>(0));
        dfs(nums, new ArrayList<>(), 0);
        return res;
    }
    
    List<List<Integer>> res = new ArrayList<>();
    private void dfs(int[] nums, List<Integer> subset, int index) {
    	if(index==nums.length)
    		return;
    	//不将当前元素添加到subset中
    	dfs(nums, subset, index+1);
        //将当前元素添加到subset中
    	if(index>0 && nums[index]==nums[index-1]) {
    		int subCount=0, i=subset.size()-1;
    		while(i>=0 && subset.get(i)==nums[index]) {
    			subCount++;
    			i--;
    		}
    		int count=0, j=index-1;
    		while(j>=0 && nums[j]==nums[index]) {
    			count++;
    			j--;
    		}
    		if(subCount!=count)
    			return;
    	}
    	List<Integer> temp = new ArrayList<>();
    	for(int e: subset)
    		temp.add(e);
    	temp.add(nums[index]);
    	res.add(temp);
    	dfs(nums, temp, index+1);
    }
}

熟悉回溯算法的思路之后可以用同样的方法求解各种题目。

51. N皇后

class Solution {
    public List<List<String>> solveNQueens(int n) {
        setPos(n, 0, new ArrayList<String>(n));
        return res;
    }
    private List<List<String>> res = new ArrayList<>();
    private void setPos(int n, int row, List<String> solution) {
    	if(row == n) {
    		addSolution(solution);
    		return;
    	}
    	outter:
    	for(int k=0;k<n;k++) {
    		for(int i=0;i<solution.size();i++) {
    			String temp = solution.get(i);
    			int j = temp.indexOf('Q');
    			if(j==k || Math.abs(i-row)==Math.abs(j-k))
    				continue outter;
    		}
    		solution.add(buildRow(n, k));
			setPos(n, row+1, solution);
			solution.remove(row);
    	}
    }
    private void addSolution(List<String> solution){
    	List<String> temp = new ArrayList<>();
    	for(String e: solution) {
    		temp.add(e);
    	}
		res.add(temp);
		return;
    }
    private String buildRow(int n, int positionOfQueen) {
    	StringBuilder s = new StringBuilder();
		for(int count=0;count<positionOfQueen;count++) {
			s.append('.');
		}
		s.append('Q');
		while(s.length()<n)
			s.append('.');
		return s.toString();
    }
}

22. 括号生成

class Solution {
    public List<String> generateParenthesis(int n) {
        addParenthesis(n, new StringBuilder(), 0, 0);
        return res;
    }
    private List<String> res = new ArrayList<>();
    private void addParenthesis(int n, StringBuilder temp, int leftCount, int rightCount) {
    	if(leftCount + rightCount == 2 * n) {
    		res.add(temp.toString());
    	}else {
        	//add left parenthesis
        	if(leftCount < n) {
        		temp.append('(');
        		addParenthesis(n, temp, leftCount+1, rightCount);
        	}
        	//add right parenthesis
        	if(leftCount > rightCount) {
        		temp.append(')');
        		addParenthesis(n, temp, leftCount, rightCount+1);
        	}
    	}
    	if(leftCount + rightCount > 0)
    		temp.deleteCharAt(temp.length()-1);
    }
}

1079.活字印刷

class Solution {
    public int numTilePossibilities(String tiles) {
        for(int i=0;i<tiles.length();i++) {
        	char c = tiles.charAt(i);
        	remainingLetters.put(c, remainingLetters.getOrDefault(c, 0)+1);
        }
        count();
        return res;
    }
    Map<Character, Integer> remainingLetters = new HashMap<>();
    int res=0;
    private void count() {
    	for(char c: remainingLetters.keySet()) {
    		if(remainingLetters.get(c) != 0) {
    			res++;
    			remainingLetters.put(c, remainingLetters.get(c)-1);
    			count();
    			remainingLetters.put(c, remainingLetters.get(c)+1);
    		}
    	}
    	return;
    }
}

216. 组合总和 III

class Solution{
    public List<List<Integer>> combinationSum3(int k, int n) {
        findCombination(k, n, new ArrayList<Integer>(), 0, 0, 0);
        return res;
    }
    List<List<Integer>> res = new ArrayList<>();
    private void findCombination(int k, int n, List<Integer> list, int count, int pre, int sum) {
    	if(count==k) {
    		if(sum==n) {
    			addToResult(list);
    		}
    		return;
    	}
    	if(sum>=n)
    		return;
    	for(int i=pre+1;i<=9;i++) {
    		list.add(i);
    		findCombination(k, n, list, count+1, i, sum+i);
    		list.remove(list.size()-1);
    	}
    	return;
    }
    private void addToResult(List<Integer> list) {
    	List<Integer> temp = new ArrayList<>(list.size());
    	for(int e: list)
    		temp.add(e);
    	res.add(temp);
    	return;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值