回溯法
回溯法公式
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
这个故事还是从全排列说起
class Solution {
private List<List<Integer>> res = new ArrayList<>();
private List<Integer> com = new ArrayList<>();
public List<List<Integer>> permute(int[] nums) {
int count = nums.length;
int[] flag = new int[count];
for(int i =0;i<count;i++)
{
flag[i] = 0;
}
backtrack(nums,flag,0,count);
return res;
}
private void backtrack(int[] nums,int[] flag,int first,int end)
{
if(first == end)
{
res.add(new ArrayList<>(com));
return;
}
for(int i=0;i<end;i++)
{
if(flag[i] == 1)
continue;
flag[i] = 1;
com.add(nums[i]);
backtrack(nums,flag,first+1,end);
com.remove(com.size()-1);
flag[i] = 0;
}
}
}
交换法交换法,我觉得交换法比标记法更容易理解,不懂的时候调试一波
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
List<Integer> output = new ArrayList<Integer>();
for (int num : nums) {
output.add(num);
}
int n = nums.length;
backtrack(n, output, res, 0);
return res;
}
public void backtrack(int n, List<Integer> output, List<List<Integer>> res, int first) {
// 所有数都填完了
if (first == n) {
res.add(new ArrayList<Integer>(output));
}
for (int i = first; i < n; i++) {
// 动态维护数组
Collections.swap(output, first, i);
// 继续递归填下一个数
backtrack(n, output, res, first + 1);
// 撤销操作
Collections.swap(output, first, i);
}
}
}
开心呢字符串,真的很不开心,思想和全排列类似,注意不需要保存数组
class Solution
{
String res = "";
int index = 0;
public String getHappyString(int n,int k)
{
StringBuilder sb = new StringBuilder();
BackTrack(sb,n,k);
return res;
}
public void BackTrack(StringBuilder sb,int n,int k)
{
if(index>=k)
return;
int len = sb.length();
if(len == n)
{
index++;
if(index==k)
{
res = sb.toString();
}
return;
}
for(int i =0;i<3;i++)
{
if(len>0&&sb.charAt(len-1)==(char)('a'+i))
continue;//查重
sb.append((char)('a'+i));
BackTrack(sb,n,k);
sb.deleteCharAt(len);
}
}
}
分割回文串
做这道题的时候,顺便还能把动态规划复习一遍,回溯法大体是上面的那个思路,理清集合之间的关系就好了
class Solution {
public List<List<String>> partition(String s) {
char[] chars = s.toCharArray();
int n = chars.length;
//System.out.println(n);
boolean [][]dp = new boolean[n][n];
//下面的两个循环结构已经找出了所有的回文串了
for(int i =0;i<n;i++)
{
dp[i][i] = true;
}
for(int j=1;j<n;j++)
{
for(int i =0;i<j;i++)
{
dp[i][j] = (chars[i] == chars[j])&&((j-i<=2)||(dp[i+1][j-1]));
}
}
List<List<String>> list = new ArrayList<>();
BackTrack(0,n,s,dp,new ArrayList<>(),list);
return list;
}
public void BackTrack(int _start,int _end,String s,boolean[][] dp,List<String> tmp,List<List<String>> list)
{
if(_start>=_end)
{
list.add(new ArrayList<>(tmp));
return;
}
boolean[] dp1 = dp[_start];
for(int i = _start;i<_end;i++)
{
if(dp1[i])
{
tmp.add(s.substring(_start,i+1));
BackTrack(i+1,_end,s,dp,tmp,list);
tmp.remove(tmp.size()-1);
}
}
return ;
}
}
单词拆分2
class Solution {
private List<String> list;
public List<String> wordBreak(String s, List<String> wordDict) {
if(s==null||s.length() == 0)
return list;
if(wordDict==null|wordDict.size()==0)
return list;
Map<Integer, List<List<String>>> map = new HashMap<Integer, List<List<String>>>();
List<List<String>> wordBreaks = backtrack(s, s.length(), new HashSet<String>(wordDict), 0, map);
List<String> list = new LinkedList<String>();
if(!check(s,wordDict))
return list;
for (List<String> wordBreak : wordBreaks) {
list.add(String.join(" ", wordBreak));
}
return list;
}
public List<List<String>> backtrack(String s, int length, Set<String> wordSet, int index, Map<Integer, List<List<String>>> map) {
if (!map.containsKey(index)) {
List<List<String>> wordBreaks = new LinkedList<List<String>>();
if (index == length) {
wordBreaks.add(new LinkedList<String>());
}
for (int i = index + 1; i <= length; i++) {
String word = s.substring(index, i);
if (wordSet.contains(word)) {
List<List<String>> nextWordBreaks = backtrack(s, length, wordSet, i, map);
for (List<String> nextWordBreak : nextWordBreaks) {
LinkedList<String> wordBreak = new LinkedList<String>(nextWordBreak);
wordBreak.offerFirst(word);
wordBreaks.add(wordBreak);
}
}
}
map.put(index, wordBreaks);
}
return map.get(index);
}
private boolean check(String s,List<String> wordDict)
{
boolean [] dp = new boolean[s.length()+1];
dp[0] = true;
for(int i=1;i<=s.length();i++)
{
for(int j=0;j<i;j++)
{
String str = s.substring(j,i);
if(dp[j]&&wordDict.contains(str))
{
dp[i]=true;
break;
}
}
}
return dp[s.length()];
}
}