目录
一:分治和回溯概念总结
1.1 分治和回溯定义:
分治:就是把大问题分解成重复的小问题。
回溯:采用试错的思想,它尝试分步去解决一个问题,在分布解决问题的过程中,
当它发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,
再通过其它可能的分步再次尝试寻找问题的答案。
1.2 分治模板
def divide_conquer(problem,param1,param2,...)
#recursion terminator
if problem is None:
print_result
return
# prepare data
data = prepare_data(problem)
subproblems = split_problem(problem,data)
# conquer subproblems
subresult1 = self.divide_conquer(subproblems[0],p1,...)
subresult2 = self.divide_conquer(subproblems[1],p1,...)
....
# process and generate the final result
result = process_result(subresult1,subresult2,...)
题目一:子集
(题目链接:https://leetcode-cn.com/problems/subsets/)
给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
思路:明显使用分治,以上面例子为例:初始化一个列表,该列表有三个位置,
三个位置可以放nums中对应元素,也可以不放,把三个位置走完,我们可能的结果就出来了。
如何实现可以放元素和不放元素-->先放该对应元素,继续调用递归,然后把该元素删除,继续递归
class Solution {
List<List<Integer>> res = new ArrayList<List<Integer>>();
List<Integer> temp = new ArrayList<Integer>();
public List<List<Integer>> subsets(int[] nums) {
recur(nums,0,new ArrayList<Integer>());
return res;
}
public List<List<Integer>> recur(int[] nums, int level, List<Integer> curr){
if(level == nums.length){
res.add(new ArrayList<Integer>(curr));
return res;
}
curr.add(nums[level]);
recur(nums,level+1,curr);
curr.remove(curr.size()-1);
recur(nums,level+1,curr);
return res;
}
}
第二次写的代码:
class Solution {
List<Integer> temp = new ArrayList<Integer>();
List<List<Integer>> res = new ArrayList<List<Integer>>();
public List<List<Integer>> subsets(int[] nums) {
dfs(0,nums);
return res;
}
public void dfs(int cur, int[] nums){
if(cur == nums.length){
List<Integer> temp2 = new ArrayList<Integer>(temp);
res.add(temp2);
return;
}
temp.add(nums[cur]);
dfs(cur+1,nums);
temp.remove(temp.size()-1);
dfs(cur+1,nums);
}
}
题目二:电话号码的字母组合
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母
输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]
思路:还是以上述样例来阐述我们的解题思路:首先字符串“23”是两位,相当于我们有两个位置需要放置东西,
每个位置怎么放:“23”的第一位是字符2,所以可以放置字符2对应的字符串“abc”中对应的某一个,那么这个该如何放?
我们只有先把“abc”怎么放的问题解决掉才能去解决外面的字符3对应的位置怎么放。
于是对于“abc”怎么放:我们采用一个for循环,每次放一个,例如放a,然后调用递归,去放外面字符3对应的位置。调用递归之后,
把刚刚放进去的a取出来,接着放b,接着递归,就形成了这种for循环里面调用递归的方式。
核心代码:
for(int i =0;i<curr_s.length();i++){
temp.append(curr_s.charAt(i));
dfs(digits,level+1,temp,map1);
temp.deleteCharAt(level);
}
class Solution {
List<String> res = new ArrayList<String>();
public List<String> letterCombinations(String digits) {
Map<Character,String> map1 = new HashMap<Character,String>();
map1.put('2',"abc");
map1.put('3',"def");
map1.put('4',"ghi");
map1.put('5',"jkl");
map1.put('6',"mno");
map1.put('7',"pqrs");
map1.put('8',"tuv");
map1.put('9',"wxyz");
dfs(digits,0, new StringBuffer(),map1);
return res;
}
public List<String> dfs(String digits,int level,StringBuffer temp,Map<Character,String> map1) {
if(digits.length() == 0){
return res;
}
if(level == digits.length()){
//res.add(new String(temp.toString()));
res.add((temp.toString()));
return res;
}
char digit = digits.charAt(level);
String curr_s = map1.get(digit);
for(int i =0;i<curr_s.length();i++){
temp.append(curr_s.charAt(i));
dfs(digits,level+1,temp,map1);
temp.deleteCharAt(level);
}
return res;
}
}
题目三:长度为n的开学字符串中字典序第k小的字符串
(题目链接:https://leetcode-cn.com/problems/the-k-th-lexicographical-string-of-all-happy-strings-of-length-n/)
一个 「开心字符串」定义为:
仅包含小写字母 ['a', 'b', 'c'].
对所有在 1 到 s.length - 1 之间的 i ,满足 s[i] != s[i + 1] (字符串的下标从 1 开始)。
比方说,字符串 "abc","ac","b" 和 "abcbabcbcb" 都是开心字符串,但是 "aa","baa" 和 "ababbc" 都不是开心字符串。
给你两个整数 n 和 k ,你需要将长度为 n 的所有开心字符串按字典序排序。
请你返回排序后的第 k 个开心字符串,如果长度为 n 的开心字符串少于 k 个,那么请你返回 空字符串 。
示例 1:
输入:n = 1, k = 3
输出:"c"
解释:列表 ["a", "b", "c"] 包含了所有长度为 1 的开心字符串。按照字典序排序后第三个字符串为 "c" 。
思路:定义一个“abc”的字符串每次可以从“abc”中取元素,最后当取到的元素形成的字符串长度到达条件的时候递归结束。
方法一:此处是把所有长度为n的字符串找到再进行过滤
class Solution {
List<String> result1 = new ArrayList<String>();
public String getHappyString(int n, int k) {
String s = "abc";
backtrack(s,0,n,new StringBuffer());
if(result1.size()>=k){
return result1.get(k-1);
}
return "";
//System.out.println(result1);
}
public void backtrack(String s, int level, int n, StringBuffer curr){
if(level == n){
boolean flag = true;
if(curr.length()==1 ){
result1.add(new String(curr.toString()));
}else if(curr.length()>1){
for(int m=0;m<curr.length()-1;m++){
if(curr.charAt(m)==curr.charAt(m+1)){
flag = false;
}
}
if(flag){
result1.add(new String(curr.toString()));
}
}
return;
}
for(int i =0;i<s.length();i++){
curr.append(s.charAt(i));
backtrack(s,level+1,n,curr);
curr.deleteCharAt(curr.length()-1);
}
}
}
方法二:在生成字符串的时候就只产生哪些符合条件的字符串
class Solution {
public String getHappyString(int n, int k) {
String s1 = "abc";
List<String> res = new ArrayList<String>();
backtrack(s1,n,new StringBuffer(),res);
if(res.size()>=k){
return res.get(k-1);
}else{
return "";
}
}
public void backtrack(String s1, int n,StringBuffer curr,List<String> res){
if(curr.length() == n){
res.add(new String(curr.toString()));
return;
}
for(int i =0;i<s1.length();i++){
if(curr.length() == 0){
curr.append(s1.charAt(i));
backtrack(s1,n,curr,res);
curr.deleteCharAt(curr.length()-1);
}
else if(curr.length()>0){
if(curr.charAt(curr.length()-1) != s1.charAt(i)){
curr.append(s1.charAt(i));
backtrack(s1,n,curr,res);
curr.deleteCharAt(curr.length()-1);
}
}
}
}
}