针对LeetCode上深度优先遍历DFS一类的排列题,做了一点总结。就当给之后自己复习留下的笔记。
LeetCode.46全排列
题解:
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList();
boolean[] pb = new boolean[nums.length];
dfs(nums,pb,new ArrayList<Integer>(),res);
return res;
}
void dfs(int[] nums,boolean[] pb,ArrayList<Integer> chain,List<List<Integer>> res){
//截止条件
if(chain.size()==nums.length){
res.add(new ArrayList(chain));
}
//循环条件
for(int i=0;i<nums.length;i++){
int c = nums[i];
if(!pb[i]){
chain.add(c);
pb[i]=true;
dfs(nums,pb,chain,res);
chain.remove(chain.size()-1);
pb[i]=false;
}
}
}
}
LeetCode47.全排列II
题解:
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
//和上一题区别,有重复第一层只可以1,2两种选择-->计数
Map<Integer,Integer> m = new HashMap();
for(int i:nums){//利用哈希man存每个字符出现的次数
m.put(i,m.containsKey(i)?m.get(i)+1:1);}
int len = m.size();
int[] p = new int[len];
int[] pb = new int[len];
int[] index =new int[1];
m.forEach((k,v)->{//这里foreach必须要是final的
p[index[0]]=k;
pb[index[0]]=v;
index[0]++;
});
List<List<Integer>> res = new ArrayList();
dfs(nums.length,p,pb,new ArrayList<Integer>(),res);
return res;
}
void dfs(int size,int[] p,int[] pb,ArrayList<Integer> chain,List<List<Integer>> res){
//截止条件
if(chain.size()==size){
res.add(new ArrayList(chain));
}
//循环条件
for(int i=0;i<p.length;i++){
int c = p[i];
if(pb[i]>0){//筛选条件
chain.add(c);
pb[i]--;
dfs(size,p,pb,chain,res);
chain.remove(chain.size()-1);
pb[i]++;
}
}
}
}
面试题08.07 无重复字符串的排列组合
题解:
class Solution {
public String[] permutation(String S) {
List<String> res = new ArrayList<>();
boolean[] pb = new boolean[S.length()];
dfs(S,pb,new StringBuilder(),res);
return res.toArray(new String[res.size()]);//指定长度用[]中括号
}
void dfs(String str,boolean[] pb,StringBuilder chain,List<String> res){
//截止条件
if(chain.length()==str.length()){
res.add(new String(chain.toString()));
return;
}
//循环条件
for(int i=0;i<str.length();i++){
char c = str.charAt(i);
if(!pb[i]){
pb[i] = true;
chain.append(c);//别忘记添加进chain
dfs(str,pb,chain,res);
chain.deleteCharAt(chain.length()-1);
pb[i] = false;
}
}
}
}
面试题08.08有重复字符串的排列组合
题解:
class Solution {
public String[] permutation(String S) {
//这种有重复元素的排序,先排序,排序完剪枝!
//剪枝只要在dfs的时候,跳过特定的值就可以contiune
List<String> res = new ArrayList<>();
char[] chars = S.toCharArray();
Arrays.sort(chars);//数组排序
boolean[] pb = new boolean[S.length()];//默认全为false
dfs(chars,pb,new StringBuilder(),res);
return res.toArray(new String[res.size()]);//指定长度用[]中括号
}
void dfs(char[] chars,boolean[] pb,StringBuilder chain,List<String> res){
//截止条件
if(chain.length()==chars.length){
res.add(new String(chain.toString()));
return;
}
//循环条件
for(int i=0;i<chars.length;i++){
char c = chars[i];
//底下为剪枝条件,可以理解为遍历到就直接跳过,不往下画子树了
if(pb[i]) //使用过直接跳过
continue;
if(i>0&&chars[i]==chars[i-1]&&!pb[i-1]) //chars[i]==chars[i - 1]判断是否与上一个遍历值相同,避免重复
continue;//!flag[i - 1] 判断是不是在同一层,只有在一层才会pb为false,子节点上都为true
//正常画子树
pb[i] = true;
chain.append(c);//别忘记添加进chain
dfs(chars,pb,chain,res);
chain.deleteCharAt(chain.length()-1);
pb[i] = false;
}
}
}
剑指offer 38.字符串排列
题解:
class Solution {
public String[] permutation(String s) {
List<String> res = new ArrayList<>();
char[] chars = s.toCharArray();
boolean[] pb = new boolean[chars.length];
Arrays.sort(chars);
dfs(chars,pb,new StringBuilder(),res);
//return res.toArray(new String[res.size()]);//这一句写错了开始
return res.toArray(new String[0]);//这里只要传进去泛型参数,返回的就是该泛型String类型的数组
}
void dfs(char[] chars,boolean[] pb,StringBuilder chain,List<String> res){
//截止条件
if(chain.length()==chars.length){
res.add(new String(chain.toString()));//这句也好写错
return;
}
//循环条件
for(int i=0;i<chars.length;i++){
char c = chars[i];
if(pb[i]) continue;
if(i>0&&chars[i]==chars[i-1]&&!pb[i-1]) continue;
pb[i]=true;
chain.append(c);
dfs(chars,pb,chain,res);
pb[i]=false;
chain.deleteCharAt(chain.length()-1);
}
}
}
784 字母大小写全排列
以下为错误写法:
class Solution {
public List<String> letterCasePermutation(String S) {
List<String> res = new ArrayList<>();
int dis = 'a'-'A';//这是大小写转换的关键,加减固定值。然后类型转换为char
char[] chars = S.toCharArray();
dfs(chars,0,new StringBuilder(),res);
return res;
}
//怎么解决原始大小写和改变大小写的都加入遍历呢????
void dfs(char[] chars,int level,StringBuilder sb,List<String> res){
//截止条件
if(sb.length()==chars.length){
res.add(new String(sb.toString()));
return;
}
//循环条件
for(int i=level;i<chars.length;i++){
char c = chars[i];
if(c>='0'&&c<='9'){
sb.append(c);
System.out.println(sb+"第几轮"+i);
continue;
}
else if(c>='A'&&c<='Z'){
c =(char)(c+32);
sb.append(c);
dfs(chars,i+1,sb,res);
c =(char)(c-32);
sb.deleteCharAt(sb.length()-1);//这句就错了,返回来的就到底了不是这一个字符了,删除末尾又添加时不对的//更新不是这句错了,而是最后一句时数字的话会跳不出循环
sb.append(c);
}
else{
c =(char)(c-32);
sb.append(c);
dfs(chars,i+1,sb,res);
sb.deleteCharAt(sb.length()-1);
c =(char)(c+32);
sb.deleteCharAt(sb.length()-1);
sb.append(c);
}
}
}
}
这题真的好不一样啊,和平时写的DFS都不一样,首先发现的问题就是他不是单一的往下遍历,而是碰到字符就会分成两个叉往下继续,但是这个字母是在同一位置的或者说同一层;除此以外,按照原始的截止条件判断就会导致当最后一个是数字结尾时遍历会在循环中结束而无法判断是否达到截止条件。现在修改成为了是就是只要有一点修改就要及时添加到最终的数组中而不是等达到截止添加才add。注意下,String.valueOf()用法,和java7引入的泛型类型推断。
正确写法:
class Solution {
public List<String> letterCasePermutation(String S) {
List<String> res = new ArrayList<>();
int dis = 'a'-'A';//这是大小写转换的关键,加减固定值。然后类型转换为char
char[] chars = S.toCharArray();
dfs(chars,0,res);
return res;
}
//怎么解决原始大小写和改变大小写的都加入遍历呢????这题不是遍历到截止条件添加,而是每次遍历都添加但是要保证不重复
void dfs(char[] chars,int level,List<String> res){
res.add(String.valueOf(chars));
//循环条件
for(int i=level;i<chars.length;i++){
if(chars[i]>='0'&&chars[i]<='9'){
continue;
}
else if(chars[i]>='A'&&chars[i]<='Z'){
chars[i] =(char)(chars[i]+32);
dfs(chars,i+1,res);
chars[i] =(char)(chars[i]-32);
}
else{
chars[i] =(char)(chars[i]-32);
dfs(chars,i+1,res);
chars[i] =(char)(chars[i]+32);
}
}
}
}