Leetcode算法系列八(搜索)

例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;
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值