Leetcode刷题模板总结-回溯算法
回溯算法是较为经典的几个算法之一,经典的问题有八皇后等,今天做leetcode发现一道经典的回溯问题,所以总结一下回溯问题的解题模板。此外做题过程中还发现一直困惑的一个点,即每一次回溯的状态都要new一个新的对象开销太大的问题,下面会仔细分析
Leetcode题目为面试题 08.07
题目很简单,就是需要列出几个字母的全排列,和八皇后或者2020年蓝桥杯省赛电子管的那道题相似,都是回溯的题目,所以这里给出回溯JAVA用递归实现的模板
// List<Object> res = new ArrayList<>();
// public void backtrack(路径, 可选列表) {
// if (终止条件) {
// 加入 res;
// return;
// }
// for (选择:选择列表) {
// 做出选择;
// backtrack(路径, 选择列表);
// 撤销选择;
// }
// }
下面是未优化前的解答:
private List<String> result;
public String[] permutation(String S) {
result=new LinkedList<>();
char[] arr=S.toCharArray();
Set<Character> totalSet=new HashSet<>();
for(char c:arr) totalSet.add(c);
track(totalSet,new LinkedList<>());
return result.toArray(new String[result.size()]);
}
private void track(Set<Character> set,List<Character> list){
if(set.isEmpty()){
StringBuilder sb=new StringBuilder();
for(char c:list){
sb.append(c);
}
result.add(sb.toString());
return;
}
for(char c : set){
Set<Character> newSet=new HashSet<>(set);
List<Character> newList=new LinkedList<>(list);
newSet.remove(c);
newList.add(c);
track(newSet,newList);
}
}
解释解释一下大概的思路,核心的代码在track方法,两个参数分别是:
set:可以使用的char集合
list:已经排好的一个序列
track方法主要有两部分,第一部分if终止条件,用来将list中的字符拼成String并添加到全局变量上,第二部分遍历set集合,开始下一轮回溯。最终耗时排名5%,空间24%。
做完后反思了下,可以有以下几个优化的点
1、全局变量可以用一个String[] 变量,因为长度为全排列的个数,这个是固定的可以计算出来
2、用空间换了时间,for遍历的时候用set省去了不必要的遍历,但是结果证明这样性能并没有得到提升
3、(重点)每一次回溯遍历都new了set和list对象,非常占空间,且list和set操作也比数组耗时长
对于第三点,就是开头说的每一次回溯都要保留一次状态,开销太大的问题,后面翻了下题解,发现一个比较好的解决方案,可以不用多个set保存可以使用的字符,改成用一个全局的boolean[],list改成了StringBuilder,这么做比set和list要好的多
这里只截取了部分片段,题解中解决多个set开销太大的办法是用一个对象来保存状态,使用完之后要撤销该次操作,所以不会影响下一次遍历递归。