算法之美-树形问题的递归与回溯法

算法之美-树形问题的递归与回溯法

递归调用的重要特征,每一层递归调用结束后要返回上一层继续调用,直到根节点的递归调用所有可能性都尝试完成,则整个递归完成,也被称为回溯。

沿着一条路径寻找答案,一旦找到了答案或者没有找到答案,一旦到底回去,继续寻找的过程,回溯法是暴力解法的主要实现实现手段。

经典案例

回溯法搜索整个数查找某个解

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例:

输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。

思路一:

方法:回溯

回溯是一种通过穷举所有可能情况来找到所有解的算法。如果一个候选解最后被发现并不是可行解,回溯算法会舍弃它,并在前面的一些步骤做出一些修改,并重新尝试找到可行解。

给出如下回溯函数 backtrack(combination, next_digits) ,它将一个目前已经产生的组合 combination 和接下来准备要输入的数字 next_digits 作为参数。

如果没有更多的数字需要被输入,那意味着当前的组合已经产生好了。 如果还有数字需要被输入: 遍历下一个数字所对应的所有映射的字母。 将当前的字母添加到组合最后,也就是 combination = combination + letter 。 重复这个过程,输入剩下的数字: backtrack(combination + letter, next_digits[1:]) 。

class Solution {
  Map<String, String> phone = new HashMap<String, String>() {{
    put("2", "abc");
    put("3", "def");
    put("4", "ghi");
    put("5", "jkl");
    put("6", "mno");
    put("7", "pqrs");
    put("8", "tuv");
    put("9", "wxyz");
  }};

  List<String> output = new ArrayList<String>();

  public void backtrack(String combination, String next_digits) {
    // if there is no more digits to check
    if (next_digits.length() == 0) {
      // the combination is done
      output.add(combination);
    }
    // if there are still digits to check
    else {
      // iterate over all letters which map 
      // the next available digit
      String digit = next_digits.substring(0, 1);
      String letters = phone.get(digit);
      for (int i = 0; i < letters.length(); i++) {
        String letter = phone.get(digit).substring(i, i + 1);
        // append the current letter to the combination
        // and proceed to the next digits
        backtrack(combination + letter, next_digits.substring(1));
      }
    }
  }

  public List<String> letterCombinations(String digits) {
    if (digits.length() != 0)
      backtrack("", digits);
    return output;
  }
}

思路二:

与字符串相关,需要考虑字符串的边界问题(字符串的合法性,空字符串)

多个解的顺序

树形问题的递归与回溯

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Comb {
static String[] LetterMap= {
        " ",    //0
        "",     //1
        "abc",  //2
        "def",  //3
        "ghi",  //4
        "jkl",  //5
        "mno",  //6
        "pqrs", //7
        "tuv",  //8
        "wxyz"  //9
};
static List<String> reStrings;
@SuppressWarnings("resource")
public static void main(String[] args) {
	Scanner scanner = new Scanner(System.in);
	String digits = scanner.next();
	if(digits.equals("")) {
		return;
	}
	reStrings = new ArrayList<>();
	 findCombination(digits,0,"");
	 System.out.println(reStrings.toString());
}
private static void findCombination(String digits, int i, String string) {
	// TODO Auto-generated method stub
	if(i==digits.length()) {
		reStrings.add(string);
		return;
	}
	Character character = digits.charAt(i);
	assert character.compareTo('0')>=0&&character.compareTo('9')<=0&&character.compareTo('1')!=0;
	String letters = LetterMap[character-'0'];
	for(int j=0;j<letters.length();j++) {
		findCombination(digits,i+1,string+letters.charAt(j));
	}
	return;
}
}

输出:

回溯法案例

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

示例:

输入: "25525511135"
输出: ["255.255.11.135", "255.255.111.35"]
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class IpAddress {
public static void main(String[] args) {
	Scanner scanner = new Scanner(System.in);
	String string = scanner.next();
	if(string==null||string.length()<4) {
		System.out.println(string);
	}
	List<String> res = new ArrayList<>();
	restoreIpAddress(string.toCharArray(),0,0,new ArrayList<>(),res);
	System.out.println(res);		
	}
/*
 * ["0.10.0.10","0.100.1.0"] 注意这个case
 */
private static void restoreIpAddress(char[] chars, int index, int size, List<String> temp, List<String> res) {
	// TODO Auto-generated method stub
	if(index>=chars.length) {
	return;	
	}
	if(size==3) {
		if(check255(chars,index,chars.length-1)) {
			res.add(temp.get(0)+"."+temp.get(1)+"."+temp.get(2)+"."+new String(chars,index,chars.length-index));
		}
		return;
	}
	
	int offset = index;
	if(chars[offset]=='0') {
		temp.add(new String(chars, index, offset-index+1));
		restoreIpAddress(chars,offset+1,size+1,temp,res);
		temp.remove(temp.size()-1);
		offset++;
	}else {
		while(offset<=chars.length-1&&check255(chars,index,offset)) {
			temp.add(new String(chars, index, offset-index+1));
			restoreIpAddress(chars,offset+1,size+1,temp,res);
			temp.remove(temp.size()-1);
			offset++;
		}
	}
}
private static boolean check255(char[] chars, int start, int end) {
	// TODO Auto-generated method stub
	if(chars[start]=='0') {
	if(start==end) {
		return true;
	}else {
		return false;
	}
	}
	int sum=0;
	for(int i=start;i<=end;i++) {
		sum  = sum*10+(chars[i]-'0');
		if(sum>255) {
			return false;
		}
	}
	return true;
}
}

输出:

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;



public class IP {
	static List<String> res;
public static void main(String[] args) {
	Scanner scanner = new Scanner(System.in);
	String string = scanner.next();
	if(scanner==null||string.length()<4)
	{
		System.out.println(string);
		return;	
	}	
	res = new ArrayList<>();
	DFS(string,0,0,new String());
	System.out.println(res);
	
}
private static void DFS(String string, int index, int N, String temp) {
	// TODO Auto-generated method stub
	if(index==string.length()) {
		if(N==4) {
			String newStr = new String(temp.substring(0, temp.length()-1));
			res.add(newStr);
		}
		return;
	}
	if(N>4) return; 
	 //如果当前为0,那么必须占用一次机会
	if(string.charAt(index)=='0') DFS(string,index+1,N+1,temp+string.charAt(index)+".");
	else {
		for(int i=1;i<=3;i++) {
			if(index+i<=string.length()) {
				String tempString = string.substring(index, index+i);
				int a = Integer.parseInt(tempString);
				if(a<255) DFS(string,index+i,N+1,temp+tempString+".");
			}
		}
	}
}
}

回溯法案例

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

返回 s 所有可能的分割方案。

示例:

输入: "aab"
输出:
[
  ["aa","b"],
  ["a","a","b"]
]
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Palindrome {
	static String string;
	static List<List<String>> totallsit;
public static void main(String[] args) {
	Scanner scanner= new Scanner(System.in);
	string =  scanner.next();
	totallsit = new ArrayList<>();
	DFS(new ArrayList<>(),0);
	System.out.println(totallsit);
}

private static void DFS(List<String> arrayList, int index) {
	// TODO Auto-generated method stub
	if(index==string.length()) {
		totallsit.add(new ArrayList<>(arrayList));
		return;
	}
	for(int i=index;i<string.length();i++) {
		if(isPalindrome(index,i)) {
			arrayList.add(string.substring(index, i+1));
			DFS(arrayList,i+1);
			arrayList.remove(arrayList.size()-1);
		}
	}
	
}

private static boolean isPalindrome(int start, int end) {
	// TODO Auto-generated method stub
	while(start<end) {
		if(string.charAt(start)!=string.charAt(end)) return false;
		start++;
		end--;
	}
	return true;
}
}

输出:

猜你喜欢

转载自blog.csdn.net/qq_35029061/article/details/90725483
今日推荐