题目1——岛屿数量
给一个01矩阵,1代表是陆地,0代表是海洋,如果两个1相邻,那么这两个1属于同一个岛,我们只考虑上下左右相邻。
示例
输入:
[
[1,1,0,0,0],
[0,1,0,1,1],
[0,0,0,1,1],
[0,0,0,0,0],
[0,0,1,1,1]
]
输出:3
解题思路
深度优先搜索一般用于树或者图的遍历,有其他分支的,比如二维矩阵也适用。
由题目可知,上下左右相邻为1的属于一个小岛,我们可以考虑每次找到一堆相邻的1后,将它们置为0,这样就不会重复统计了。
当遇到矩阵中某个元素为1时,首先将其置0,然后查看与它相邻的四个方向,如果这四个方向中元素为1,则进入该元素,重复解决上述的子问题,可以使用递归实现。
具体做法:
- 首先判断矩阵为空的情况;
- 依次遍历矩阵中的元素,如果该元素为1,统计岛的个数;
- 接着将该元素改为1,然后判断四个方向是否为1,若为1则递归进入子问题。
代码实现
import java.util.*;
public class Solution {
public void dfs(char[][] grid,int i,int j){
int m = grid[0].length;
int n = grid.length;
grid[i][j] = '0';
if(i-1>=0 && grid[i-1][j] == '1')
dfs(grid,i-1,j);
if(i+1<n && grid[i+1][j] == '1')
dfs(grid,i+1,j);
if(j-1>=0 && grid[i][j-1] == '1')
dfs(grid,i,j-1);
if(j+1<m && grid[i][j+1] == '1')
dfs(grid,i,j+1);
}
public int solve (char[][] grid) {
int m = grid[0].length;
int n = grid.length;
if(n==0) return 0;
int res = 0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(grid[i][j] == '1'){
res++;
dfs(grid,i,j);
}
}
}
return res;
}
}
题目2——字符串的排列
输入一个长度为n的字符串,打印该字符串中字符的所有排列,你可以以任意顺序返回这个字符串数组。
要求:空间复杂度 O(n!),时间复杂度 O(n!)。
示例
输入:“aab”
输出:[“aab”,“aba”,“baa”]
解题思路
与有重复项数字的全排列类似,但是这道题目输出顺序没有要求,但是为了便于去掉重复情况,还是应该参照数组排列。具体做法如下:
- 对字符串按照字典排序;
- 准备一个空串暂存递归过程中字符串的排列情况,使用标记数组记录哪些位置字符被加入了;
- 每次递归从头遍历字符串,如果已经加入过当前位置的元素,继续循环;同时,如果当前元素与前一个元素相同,且前一个元素已经用过,则继续循环;
- 否则,加入字符,并标记使用过,进入下一层递归;
- 回溯的时候,需要将加入的元素删除,并修改标记未使用过;
- 当加入的字符串长度达到原字符串长度就是一种排列情况。
代码实现
import java.util.*;
public class Solution {
public ArrayList<String> Permutation(String str) {
ArrayList<String> list = new ArrayList<>();
if (str == null || str.length() == 0) {
return list;
}
char[] chars = str.toCharArray();
Arrays.sort(chars);
boolean[] used = new boolean[str.length()];
StringBuilder sb = new StringBuilder();
backTrack(sb, list, used, chars);
return list;
}
public void backTrack(StringBuilder path, ArrayList<String> res, boolean[] used, char[] chars){
if (path.length() == chars.length) {
res.add(path.toString());
return;
}
for (int i = 0; i < chars.length; i++) {
if (used[i] || (i > 0 && chars[i] == chars[i - 1] && !used[i - 1])) {
continue;
}
path.append(chars[i]);
used[i] = true;
backTrack(path, res, used, chars);
used[i] = false;
path.deleteCharAt(path.length() - 1);
}
}
}
题目3——N皇后问题
N皇后问题说指在n*n的棋盘上要摆n个皇后,要求:任何两个皇后不同行,不同列,页不在同一条斜线上,求给一个整数n,返回n皇后的摆法数。
要求:空间复杂度 O(1) ,时间复杂度 O(n!)。
示例
输入:4
输出:2
解题思路
n的皇后,不同行不同列,那么棋盘每行都会有一个皇后,每列都会有一个皇后,如果我们确定了一个皇后的行号与列号,就相当于在接下来的n-1行找n-1个皇后,这就是一个子问题。
当每行都放了皇后,说明找到了一种方案。
代码实现
import java.util.*;
public class Solution {
private int res;
public int Nqueen (int n) {
res = 0;
dfs(new int[n],0);
return res;
}
//nums[n],存放的是每一行放的位置
public void dfs(int[] nums,int cur){
int n = nums.length;
//cur=n表示每一行都放了皇后,找到一种方案
if(cur == n){
res++;
return;
}
//设置哪些位置可以访问
boolean[] visited = new boolean[n];
//遍历每一行,把当前行中所有会和之前皇后冲突的位置标记为不可访问
for(int i=0;i<cur;i++){
// e表示第i行到当前行的距离
int e = cur - i;
//v表示第i行皇后所在的位置
int v = nums[i];
//r表示右下方冲突的列,l表示左下方冲突的列
int r = v+e;
int l = v-e;
visited[v] = true; //将第i行皇后所在的位置还有冲突的列都设为不可访问
if(l>=0) visited[l] = true;
if(r<n) visited[r] = true;
}
//对当前行剩余的可放皇后的位置,进行递归
for(int i=0;i<n;i++){
if(visited[i]) continue;
nums[cur] = i;
dfs(nums,cur+1);
}
}
}
class Solution:
res = 0
def dfs(self,nums,cur):
n = len(nums)
#此时表示每一行都放了皇后,找到了一种方案
if cur == n:
self.res += 1
# 标记数组
visited = [False] * n
//遍历当前行之前的行,找到冲突的列
for i in range(cur):
# e表示第i行到当前行的距离
e = cur - i
v = nums[i] #第i行皇后所在的位置,比如nums[0]=1表示第一行的皇后在第2列中
# l、r表示左下方和右下方冲突的位置
l = v-e
r = v+e
# 设置这些冲突列不可访问
visited[v] = True
if l>=0:
visited[l] = True
if r<n:
visited[r] = True
# 遍历所有的位置,对当前剩余可放皇后的位置进行递归
for i in range(n):
if visited[i]: continue
nums[cur] = i
self.dfs(nums,cur+1)
def Nqueen(self , n: int) -> int:
nums = [0]*n
self.dfs(nums, 0)
return self.res