5道经典深度优先搜索与宽度优先搜索相关题目
例1:岛屿数量(medium)(深搜、宽搜)
200. 岛屿数量
难度中等
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:grid = [
[“1”,“1”,“1”,“1”,“0”],
[“1”,“1”,“0”,“1”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“0”,“0”,“0”]
]
输出:1
示例 2:
输入:grid = [
[“1”,“1”,“0”,“0”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“1”,“0”,“0”],
[“0”,“0”,“0”,“1”,“1”]
]
输出:3
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] 的值为 ‘0’ 或 ‘1’
算法伪代码
leetcode200
输入:grid数组,1表示陆地、0表示水
输出:岛屿数量
1.设置islandNum=0岛屿数量,标记数组mark[][]
2.设置一个函数BFS来对岛屿进行查找
2.1标记数组对应位置置为1,代表已经搜索该点
2.2新建一个队列queue,每次将符合条件(mark为0,grid为1)的点,加入队列
2.3新建一个方向数组
2.4循环队列,直到队列为空
2.4.1使用方向数组,取上下左右四个点
2.4.2遇到越界情况,continue
2.4.3判断当前点是否符合条件,符合则加入队列,并将对应mark数组位置标为1
3.循环grid数组,直到没有一个元素
3.1符合条件(mark为0,grid为1)的点,则调用BFS,并islandNum自增
代码
import java.util.LinkedList;
import java.util.Queue;
public class Solution {
public void BFS(char [][]grid,int [][]mark,int x,int y){
mark[x][y]=1;
Queue<String> queue=new LinkedList<>();
int xPoint[]={0,0,-1,1};
int yPoint[]={1,-1,0,0};
StringBuilder tool=new StringBuilder();
tool.append(x);
tool.append(':');
tool.append(y);
queue.add(tool.toString());
while(!queue.isEmpty()){
String[] strings=queue.poll().split(":");
for(int i=0;i<4;i++){
int newX=Integer.parseInt(strings[0])+xPoint[i];
int newY=Integer.parseInt(strings[1])+yPoint[i];
if(newX<0||newY<0||newX>=mark.length||newY>=mark[0].length){
continue;
}
if(grid[newX][newY]=='1'&&mark[newX][newY]==0){
StringBuilder tool1=new StringBuilder();
tool1.append(newX);
tool1.append(':');
tool1.append(newY);
queue.add(tool1.toString());
mark[newX][newY]=1;
}
}
}
}
public int numIslands(char[][] grid) {
int islandNum=0;
int m=grid.length,n=grid[0].length;
int mark[][]=new int[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j]=='1'&&mark[i][j]==0){
BFS(grid,mark,i,j);
islandNum++;
}
}
}
return islandNum;
}
}
例2-a:词语阶梯(medium)(宽搜、图、哈希表)
127. 单词接龙
难度困难
字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列:
序列中第一个单词是 beginWord 。
序列中最后一个单词是 endWord 。
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典 wordList 中的单词。
给你两个单词 beginWord 和 endWord 和一个字典 wordList ,找到从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0。
示例 1:
输入:beginWord = “hit”, endWord = “cog”, wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
输出:5
解释:一个最短转换序列是 “hit” -> “hot” -> “dot” -> “dog” -> “cog”, 返回它的长度 5。
示例 2:
输入:beginWord = “hit”, endWord = “cog”, wordList = [“hot”,“dot”,“dog”,“lot”,“log”]
输出:0
解释:endWord “cog” 不在字典中,所以无法进行转换。
提示:
1 <= beginWord.length <= 10
endWord.length == beginWord.length
1 <= wordList.length <= 5000
wordList[i].length == beginWord.length
beginWord、endWord 和 wordList[i] 由小写英文字母组成
beginWord != endWord
wordList 中的所有字符串 互不相同
算法伪代码
leetcode127
输入:起点单词beginWord,终点单词endWord,单词字典wordList
输出:起点到终点的最短转换步数
1.将起点单词beginWord加入到wordList中。
2.使用<String,List>map构造无向图graph
2.1为每个wordList元素建立映射,映射到空的ArrayList中
2.2遍历wordlist,找到wordlsit中元素与其他元素相差1个字符的元素,将他们加入对方的邻接表中
3.对无向图进行宽度优先搜索
3.1建立一个队列queue,一个hashset的visit
3.2循环队列,直到队列为空
3.2.1取出队列元素
3.2.2如果当前队列元素等于endWord,则返回当前步数
3.2.3取出该队列元素的邻接元素,未visit的,构造pair(string字符,int步数)加入到queue中
4.最后未找到结束单词,返回0
代码
public class Solution {
public boolean connect(String s1,String s2){
int res=0;
for(int i=0;i<s1.length();i++){
if(s1.charAt(i)!=s2.charAt(i)){
res++;
}
}
return res==1;//返回相异字符是否等于1
}
public void generateGraph(String beginWord,List<String> wordList,HashMap<String,List<String>> map){
wordList.add(beginWord);
//HashMap<String,List<String>> map=new HashMap<>();
for(int i=0;i<wordList.size();i++){
map.put(wordList.get(i),new ArrayList<>());
}
for(int i=0;i<wordList.size();i++){
for(int j=i+1;j<wordList.size();j++){
if(connect(wordList.get(i),wordList.get(j))){
//System.out.println(1);
map.get(wordList.get(i)).add(wordList.get(j));
map.get(wordList.get(j)).add(wordList.get(i));
}
}
}
// for(int i=0;i<wordList.size();i++){
// System.out.println(wordList.get(i)+":"+map.get(wordList.get(i)).toString());
// }
}
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
LinkedList<Pair<String,Integer>> queue=new LinkedList<>();
HashSet<String> visit=new HashSet<>();
HashMap<String,List<String>> map=new HashMap<>();
generateGraph(beginWord,wordList,map);//构造图
// for(int i=0;i<wordList.size();i++){
// System.out.println(map.get(wordList.get(i)).toString());
// }
visit.add(beginWord);
queue.add(new Pair(beginWord,1));
while(!queue.isEmpty()){
String word= queue.peek().getKey();
int step = queue.peek().getValue().intValue();
//System.out.println("step:"+step);
//System.out.println("size:"+queue.size());
queue.removeFirst();
if(word.compareTo(endWord)==0){
return step;
}
// System.out.println(word);
List<String> node=map.get(word);//返回该单词的邻接表
//System.out.println(node.toString());
for(int i=0;i<node.size();i++){
if(!visit.contains(node.get(i))){
queue.add(new Pair(node.get(i),step+1));
visit.add(word);
}
}
}
return 0;
}
}
例2-b:词语阶梯2(hard)(记录路径的宽搜、图、哈希表)
126. 单词接龙 II
难度困难
给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则:
每次转换只能改变一个字母。
转换后得到的单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回一个空列表。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例 1:
输入:
beginWord = “hit”,
endWord = “cog”,
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
输出:
[
[“hit”,“hot”,“dot”,“dog”,“cog”],
[“hit”,“hot”,“lot”,“log”,“cog”]
]
示例 2:
输入:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,“dot”,“dog”,“lot”,“log”]
输出: []
解释: endWord “cog” 不在字典中,所以不存在符合要求的转换序列。
算法伪代码
leetcode126
输入:起点单词beginWord,终点单词endWord,单词字典wordList
输出:起点到终点的所有最短路径
class Qitem{
string node,
int parent_pos,
int step
Qitem(){
}…
}
1.查找wordList判断,其中是否有beginword,如果无,将起点单词beginWord加入到wordList中。
2.使用<String,List>map构造无向图graph
2.1为每个wordList元素建立映射,映射到空的ArrayList中
2.2遍历wordlist,找到wordlsit中元素与其他元素相差1个字符的元素,将他们加入对方的邻接表中
3.对无向图进行宽度优先搜索
3.1建立一个队列arraylist,一个hashmap的visit(结点,步数)
3.2循环队列,直到队列到最后一个元素
3.2.1取出队列元素
3.2.2如果队列当前步数大于最小步数,则break
3.2.3如果当前队列元素等于endWord,则min_step等于当前步数,则添加队列元素qitem到end_word_pos
3.2.4取出该队列元素的邻接元素,未visit的或visit[neighbor]=step+1,则构造Qitem加入到arraylist中,更新visit对应元素
4.遍历end_word_pos
4.1每个result[i]保存1条路径
4.2从end_word_pos[i]元素的往前跳,直到pos=-1。
5.返回result
代码
import java.util.*;
public class Solution {
class Qitem{
String node;
int parent_pos;//指向父元素的队列位置
int step;
Qitem(){}
Qitem(String node,int parent_pos,int step){
this.node=node;
this.parent_pos=parent_pos;
this.step=step;
}
}
public boolean connect(String s1,String s2){
int res=0;
for(int i=0;i<s1.length();i++){
if(s1.charAt(i)!=s2.charAt(i)){
res++;
}
}
return res==1;//返回相异字符是否等于1
}
public void generateGraph(String beginWord, List<String> wordList, HashMap<String,List<String>> map){
int flag=0;
for(int i=0;i<wordList.size();i++){
if(wordList.get(i).compareTo(beginWord)==0){
flag=1;//wordList存在beginWord
}
}
if(flag==0){
wordList.add(beginWord);
}
for(int i=0;i<wordList.size();i++){
map.put(wordList.get(i),new ArrayList<>());
}
for(int i=0;i<wordList.size();i++){
for(int j=i+1;j<wordList.size();j++){
if(connect(wordList.get(i),wordList.get(j))){
map.get(wordList.get(i)).add(wordList.get(j));
map.get(wordList.get(j)).add(wordList.get(i));
}
}
}
}
public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
List<Qitem> arraylist=new ArrayList<>();//队列
HashMap<String,Integer> visit=new HashMap<>();//元素与步数
HashMap<String,List<String>> map=new HashMap<>();//图
generateGraph(beginWord,wordList,map);//构造图
visit.put(beginWord,1);
arraylist.add(new Qitem(beginWord,-1,1));//添加起点到队列中
int front=0;//指向队列的指针
int min_step=0;
List<Integer> end_word_pos=new ArrayList<>();//记录endword在队列中的位置
List<List<String>> result=new ArrayList<>();
while(front!=arraylist.size()){
//System.out.println(1);
String word= arraylist.get(front).node;
int step = arraylist.get(front).step;
if(min_step!=0&&step>min_step){
break;
}
if(word.compareTo(endWord)==0){
min_step=step;
end_word_pos.add(front);
}
//System.out.println(word);
List<String> node=map.get(word);//返回该单词的邻接表
//System.out.println(node.toString());
for(int i=0;i<node.size();i++){
if(!visit.containsKey(node.get(i))||visit.get(node.get(i))==step+1){
arraylist.add(new Qitem(node.get(i),front,step+1));
visit.put(node.get(i),step+1);
}
}
front++;
}
for(int i=0;i<end_word_pos.size();i++){
int pos=end_word_pos.get(i);
List<String> path=new ArrayList<>();
while(pos!=-1){
path.add(arraylist.get(pos).node);
pos=arraylist.get(pos).parent_pos;
}
result.add(new ArrayList<>());
for(int j=path.size()-1;j>=0;j--){
result.get(i).add(path.get(j));
}
}
return result;
}
}
例3:火柴棍摆正方形(medium)(回溯深搜、位运算)
473. 火柴拼正方形
难度中等
还记得童话《卖火柴的小女孩》吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法。不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到。
输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。
示例 1:
输入: [1,1,2,2,2]
输出: true
解释: 能拼成一个边长为2的正方形,每边两根火柴。
示例 2:
输入: [3,3,3,3,4]
输出: false
解释: 不能用所有火柴拼成一个正方形。
注意:
给定的火柴长度和在 0 到 10^9之间。
火柴数组的长度不超过15。
算法伪代码
leetcode473
输入:由火柴长度组成的数组nums
输出:能否拼成正方形(true或false)
1.如果火柴数目小于4,则返回false
2.如果火柴长度之和sum不能被4整除,则返回false
3.求出target=sum/4
4.设置火柴所有位运算可能数目all=1<<nums.length,设置所有符合情况一条边组成的动态数组one,设置所有符合情况两条边组成的动态数组two
5.使用i循环all
5.1设置count统计i对应所有位元素的和
5.2使用j循环nums.length的所有位
5.2.1(i&(1<<j)!=0)则说明i有取到该位元素,count+=nums[j]
5.3如果count==target,则push元素i到one动态数组
6.使用i循环one数组,直到结尾
6.1使用j=i+1循环one数组,直到结尾
6.1.1如果one[i]&one[j]=0(无重复元素),则可以push(one[i] | one[j])到two
7.使用i循环two数组,直到结尾
7.1使用j=i+1循环two数组,直到结尾
7.1.1如果two[i]&two[j]=0(无重复元素),则返回true
8.如果上述情况都不满足,则返回false
代码
import java.util.ArrayList;
import java.util.List;
public class Solution {
public boolean makesquare(int[] nums) {
//如果火柴数目小于4,则返回false
if(nums.length<4){
return false;
}
//如果火柴长度之和sum不能被4整除,则返回false
int sum=0;
for(int i=0;i<nums.length;i++){
sum+=nums[i];
}
if(sum%4!=0){
return false;
}
//主要算法
int target=sum/4;
int all=1<<nums.length;//相当于2的nums.length次方
List<Integer> one=new ArrayList<>();
List<Integer> two=new ArrayList<>();
for(int i=0;i<all;i++){
int count=0;
for(int j=0;j<nums.length;j++){
//(i&(1<<j)!=0)与运算,不为零,说明有相同的位,则i有取到该位元素
if((i&(1<<j))!=0){
count+=nums[j];
}
}
if(count==target){
one.add(i);
}
}
/**
*
* 6.使用i循环one数组,直到结尾
* 6.1使用j=i+1循环one数组,直到结尾
* 6.1.1如果one[i]&one[j]=0(无重复元素),则可以push(one[i] | one[j])到two
*
*/
for(int i=0;i<one.size();i++){
for(int j=i+1;j<one.size();j++){
if((one.get(i)&one.get(j))==0){
two.add(one.get(i)|one.get(j));
}
}
}
/**
*
* 7.使用i循环two数组,直到结尾
* 7.1使用j=i+1循环two数组,直到结尾
* 7.1.1如果two[i]&two[j]=0(无重复元素),则返回true
*/
for(int i=0;i<two.size();i++){
for(int j=i+1;j<two.size();j++){
if((two.get(i)&two.get(j))==0){
return true;
}
}
}
return false;
}
}
例4:收集雨水2(hard)(带优先级的宽度优先搜索、堆)
407. 接雨水 II
难度困难
给你一个 m x n 的矩阵,其中的值均为非负整数,代表二维高度图每个单元的高度,请计算图中形状最多能接多少体积的雨水。
示例:
给出如下 3x6 的高度图:
[
[1,4,3,1,3,2],
[3,2,1,3,2,4],
[2,3,3,2,3,1]
]
返回 4 。
如上图所示,这是下雨前的高度图[[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]] 的状态。
下雨后,雨水将会被存储在这些方块中。总的接雨水量是4。
提示:
1 <= m, n <= 110
0 <= heightMap[i][j] <= 20000
算法伪代码
leetcode407
输入:m x n的二维数组
输出:该几何体最多能接雨水的体积
1.构造Qitem类,mark二维数组对应heightMap数组,优先级队列(以高度划分最小堆)Q
2.将mark数组外圈的元素push到Q中,并将mark对应位置标记为1
3.设置方向数组,result=0
4.取队列中的队头元素,直到队列为空
4.1设置newx和newy,Q.poll()
4.2如果newx和newy不在合法范围内,或者已经在队列中,则continue
4.3如果h>heightMap[newx][newy]
4.3.1result+=h-heightMap[newx][newy]
4.3.2heightMap[newx][newy]=h
4.4Q.push(newQitem(newx,newy,heightMap[newx][newy]))
4.5mark[newx][newy]=1
5.输出result
代码
import java.util.Comparator;
import java.util.PriorityQueue;
public class Solution {
class Qitem{
int x;
int y;
int h;
public Qitem(int x, int y, int h) {
this.x = x;
this.y = y;
this.h = h;
}
}
public int trapRainWater(int[][] heightMap) {
int row=heightMap.length;
int column=heightMap[0].length;
int mark[][]=new int[row][column];
PriorityQueue<Qitem> Q=new PriorityQueue<>(11, new Comparator<Qitem>() {
@Override
public int compare(Qitem o1, Qitem o2) {
return o1.h-o2.h;
}
});
for(int i=0;i<row;i++){
Q.add(new Qitem(i,0,heightMap[i][0]));
mark[i][0]=1;
Q.add(new Qitem(i,column-1,heightMap[i][column-1]));
mark[i][column-1]=1;
}
for(int j=1;j<column-1;j++){
Q.add(new Qitem(0,j,heightMap[0][j]));
mark[0][j]=1;
Q.add(new Qitem(row-1,j,heightMap[row-1][j]));
mark[row-1][j]=1;
}
int result=0;
int newxs[]={0,0,-1,1};
int newys[]={1,-1,0,0};
while(!Q.isEmpty()){
int x=Q.peek().x;
int y=Q.peek().y;
int h=Q.peek().h;
Q.poll();
for(int i=0;i<4;i++){
int newx=x+newxs[i];
int newy=y+newys[i];
//如果newx和newy不在合法范围内,或者已经在队列中,则continue
if(newx<0||newx>=row||newy<0||newy>=column||mark[newx][newy]==1){
continue;
}
if(h>heightMap[newx][newy]){
result+=h-heightMap[newx][newy];
heightMap[newx][newy]=h;
}
Q.add(new Qitem(newx,newy,heightMap[newx][newy]));
mark[newx][newy]=1;
}
}
return result;
}
}