深度优先搜索(Depth-First Search,简称DFS)是一种用于遍历或搜索树或图的算法。如其名,该算法深入到可能的分支上,直到目标节点被找到或者已经没有其他节点可以继续深入,此时算法回溯到上一节点,以探索未被遍历的路径。深度优先搜索是一个递归算法,它利用了后进先出的栈结构,在图的遍历中特别有效。
DFS(node):
if node is visited:
return
mark node as visited
for each node_neighbor in node.adjacent_nodes:
if node_neighbor is not visited:
DFS(node_neighbor)
在实际编程中,深度优先搜索可以通过递归实现,也可以通过显式地使用栈来实现非递归版本。递归版本的实现较为简洁,但在遇到深度非常大的图时可能会导致栈溢出。非递归版本虽然代码复杂一些,但可以避免栈溢出的问题。
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
public static List<List<Integer>> res = new LinkedList<>();
/**
* 输入一组不重复的数字,返回它们的全排列
*
* @param nums
* @return
*/
public static List<List<Integer>> permute(int[] nums) {
// 记录「路径」
LinkedList<Integer> track = new LinkedList<>();
backtrack(nums, track);
return res;
}
public static void backtrack(int[] nums, LinkedList<Integer> track) {
if (track.size() == nums.length) {
res.add(new LinkedList<>(track));
return;
}
for (int i = 0; i < nums.length; i++) {
int num = nums[i];
if (track.contains(num)) {
continue;
}
track.add(num);
backtrack(nums, track);
track.removeLast();
}
}
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
public static List<String[][]> result = new LinkedList<>();
/**
* N皇后问题
* 如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
* 给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
*
* @param n 皇后个数
* @return 满足条件的所有方式
*/
public static List<String[][]> solveNQueens(int n) {
String[][] strings = new String[n][n];
Arrays.fill(strings, ".");
backtrack(strings, 0);
return result;
}
public static void backtrack(String[][] strings, int i) {
if (i >= strings.length) {
result.add(strings);
return;
}
for (int j = 0; j < strings.length; j++) {
if (!isValid(strings, i, j)) {
continue;
}
strings[i][j] = "Q";
backtrack(strings, i + 1);
strings[i][j] = ".";
}
}
/**
* i行j列放置皇后是否满足要求
* @param strings 棋盘布局
* @param i 行
* @param j 列
* @return 是否满足要求
*/
public static boolean isValid(String[][] strings, int i, int j) {
//判断同一行是否已经有
for (int k = 0; k < i; k++) {
if ("Q".equals(strings[i][k])) {
return false;
}
}
//判断左上方是否有
for (int m = i - 1, n = j - 1; m >= 0 && n >= 0; m--, n--) {
if ("Q".equals(strings[m][n])) {
return false;
}
}
//判断右上方是否有
for (int m = i - 1, n = j + 1; m >= 0 && n <= j; m--, n++) {
if ("Q".equals(strings[m][n])) {
return false;
}
}
return true;
}
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
public static List<List<Integer>> res2 = new LinkedList<>();
/**
* 子集问题
* 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
* 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
*
* @param nums 数组
* @return 所有可能的子集
*/
public static List<List<Integer>> subsets(int[] nums) {
LinkedList<Integer> track = new LinkedList<>();
backtrack(nums, 0, track);
return res2;
}
public static void backtrack(int[] nums, int start, LinkedList<Integer> track) {
res2.add(new LinkedList<>(track));
for (int i = start; i < nums.length; i++) {
track.add(nums[i]);
backtrack(nums, i + 1, track);
track.removeLast();
}
}
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合
public static List<List<Integer>> res3 = new LinkedList<>();
/**
* 组合问题
* 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
* @param n
* @param k
* @return 所有可能的组合
*/
public static List<List<Integer>> combine(int n, int k) {
LinkedList<Integer> track = new LinkedList<>();
backtrack(n, 1, k, track);
return res3;
}
public static void backtrack(int n, int start, int k, LinkedList<Integer> track) {
if (track.size() == k) {
res3.add(new LinkedList<>(track));
return;
}
for (int i = start; i <= n; i++) {
track.add(i);
backtrack(n, i + 1, k, track);
track.removeLast();
}
}