常见题型
遍历二维数组并且统计个数
这种题型一般通过建立方向数组,方向数组建立如下代码所示
int[][] direction = {{1,0},{-1,0},{0,1},{0,-1}};
for (int i = 0; i < 4; i++){
int nx = x + direction[i][0];
int ny = y + direction[i][1];
if(nx >= 0 && nx < row && ny >= 0 && ny < col)
{
}
}
通过建立该数组在借助循环可以实现对一个二维数组结合递归函数(一般取名dfs)达到深度优先搜索的目的。该题会给出某些位置不可遍历的条件,该条件一般作为递归出口来使用。由于通常不能重复遍历,所以也要设计一个标记数组防止重复遍历。
总结:
1.方向数组 2.标记数组防止重复遍历 3.不可访问条件作为递归出口
例题:
全排列问题
通过交换进行全排列
通过交换函数和递归函数相结合达到对字符串的重新排列,递归函数的退出条件为达到该字符串长度停止递归并且将这次结果加入集合中。还需要用结果集(HashSet)记录下每次递归使用的字符达到去重的目的。如果集合中是字符串要排序的话就比较简单,直接用Arrays.sort()即可,如果不是则要采用非交换进行全排列的方法。
总结: 1.交换函数 2.递归函数,达到字符串长度为递归出口 3.哈希集合去重 4.Arrays.sort()排序
非交换的方法
总体思路差不多,只不过没有了交换函数,新增了一个标记数组用于去重,下面用这道题为例来讲解。
有重复项数字的全排列_牛客题霸_牛客网 (nowcoder.com)
完整题解代码:
public void recursion(ArrayList<ArrayList<Integer>> res, int[] num, ArrayList<Integer> temp, Boolean[] vis){
//临时数组满了加入输出
if(temp.size() == num.length){
res.add(new ArrayList<Integer>(temp));
return;
}
//遍历所有元素选取一个加入
for(int i = 0; i < num.length; i++){
//如果该元素已经被加入了则不需要再加入了
if(vis[i])
continue;
if(i > 0 && num[i - 1] == num[i] && !vis[i - 1])
//当前的元素num[i]与同一层的前一个元素num[i-1]相同且num[i-1]已经用过了
//这个条件是用于用于开头的第一个数字的,上面那个条件是用于后面的判断
continue;
//标记为使用过
vis[i] = true;
//加入数组
temp.add(num[i]);
recursion(res, num, temp, vis);
//回溯
vis[i] = false;
//移除temp最后一个元素
temp.remove(temp.size() - 1);
}
}
public ArrayList<ArrayList<Integer>> permuteUnique(int[] num) {
//先按字典序排序
Arrays.sort(num);
Boolean[] vis = new Boolean[num.length];
Arrays.fill(vis, false);
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
ArrayList<Integer> temp = new ArrayList<Integer>();
recursion(res, num, temp, vis);
return res;
}
其实就是采用一个不断添加字符去除字符的过程,当添加达到长度限制时,即为递归出口即将此次结果添加入结果集合别返回,返回后还要进行回溯然后添加不同的字符。通过 for(int i = 0; i < num.length; i++) 和 Arrays.sort()达到按字典顺序排序的目的。
对于带有重复的字符的情况,部分代码功能如下:
总结:
1.标记数组,用于有重复字符时候的去重 2.递归函数,达到字符串长度为递归出口 3.回溯,对标记数组和字符集进行回退。