搜索问题汇总

1、BFS

广度优先搜索一层一层地进行遍历,每层遍历都是以上一层遍历的结果作为起点,遍历一个距离能访问到的所有结点。遍历过的结点不能再次被遍历。

设 di 表示第 i 个节点与根节点的距离,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di <= dj。

应用场景:无权图的最短路径

程序实现:1、队列,用来存储每一轮遍历得到的节点;2、标记,对于遍历过的节点,应该将它标记,防止重复遍历。

leetcode例题:

class Solution {
    public int shortestPathBinaryMatrix(int[][] grid) {
        if(grid==null||grid.length==0||grid[0].length==0) return -1;
        int m=grid.length,n=grid[0].length;
        //搜索时用来更新下一个点坐标
        int[][] dir={{-1,-1},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1}};
        //存储每一轮遍历到的节点坐标
        Queue<Pair<Integer,Integer>>queue=new LinkedList<>();
        //根节点入队
        queue.add(new Pair<>(0,0));
        //记录路径长度
        int pathLength=0;
        while(!queue.isEmpty()){
            //记录当前队列中的所有结点
            int size=queue.size();
            //经过一轮遍历,更新路径长度
            pathLength++;
            while(size-->0){
                //节点出队
                Pair<Integer,Integer>cur=queue.poll();
                int x=cur.getKey(),y=cur.getValue();
                //剪枝
                if(grid[x][y]==1){
                    continue;
                }
                //遍历到目标节点,返回
                if(x==m-1&&y==n-1){
                    return pathLength;
                }
                //标记该节点已被遍历
                grid[x][y]=1;
                for(int[] d:dir){
                    //更新当前节点的下一个可选节点
                    int new_x=x+d[0],new_y=y+d[1];
                    if(new_x<0||new_x>=m||new_y<0||new_y>=n){
                        continue;
                    }
                    //满足条件的下一轮节点入队
                    queue.add(new Pair<>(new_x,new_y));
                }
            }
        }
        return -1;
    }
}

2、DFS

广度优先搜索一层一层遍历,每一层得到的所有新节点,要用队列存储起来以备下一层遍历的时候再遍历。而深度优先搜索在得到一个新节点时立即对新节点进行遍历,直到没有新节点了,此时返回。

解决问题:从一个节点出发,使用DFS对一个图进行遍历时,能够遍历到的节点都是从初始节点可达的,可用来求解这种可达性问题。

程序实现:1、栈,用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。2、标记,和BFS一样同样需要对已经遍历过的节点进行标记。

leetcode例题:

class Solution {
    private int m,n;
    private int[][] dir={{0,-1},{-1,0},{0,1},{1,0}};
    public int maxAreaOfIsland(int[][] grid) {
        if(grid==null||grid.length==0||grid[0].length==0){
            return 0;
        }
        m=grid.length;
        n=grid[0].length;
        int maxArea=0;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                //遍历到为1的节点才进行DFS
                if(grid[i][j]==1){
                    maxArea=Math.max(maxArea,dfs(grid,i,j));
                }
            }
        }
        return maxArea;
    }
    private int dfs(int[][] grid,int x,int y){
        if(x<0||x>=m||y<0||y>=n||grid[x][y]==0){
            return 0;
        }
        //标记已经遍历过
        grid[x][y]=0;
        int area=1;
        for(int[] d:dir){
            area+=dfs(grid,x+d[0],y+d[1]);
        }
        return area;
    }
}

3、Backtracking

Backtracking(回溯)属于DFS。普通DFS主要用在可达性问题,这种问题只要执行到特定的位置然后返回即可。而Backtracking主要用于求解排列组合问题,这种问题在执行到特定的位置返回之后还会继续执行求解过程。

程序实现注意问题:在访问一个元素进入新的递归调用时,需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素;但是在递归返回时,需要将元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,可以访问已经访问过但是不在当前递归链中的元素。

leetcode例题:

class Solution {
    public List<String> restoreIpAddresses(String s) {
        List<String>res=new ArrayList<>();
        StringBuilder sb=new StringBuilder();
        backtracking(0,sb,res,s);
        return res;
    }
    private void backtracking(int k,StringBuilder sb,List<String>res,String s){
        //递归结束条件
        if(k==4||s.length()==0){
            if(k==4&&s.length()==0){
                res.add(sb.toString());
            }
            return;
        }
        //对于255来说,依次截取2、25、255
        for(int i=0;i<s.length()&&i<=2;i++){
            //避免分割成像01这样的字符串
            if(i!=0&&s.charAt(0)=='0'){
                break;
            }
            String part=s.substring(0,i+1);
            //判断分割的字符串是否合法
            if(Integer.parseInt(part)<=255){
                if(sb.length()!=0){
                    part="."+part;
                }
                //添加选择
                sb.append(part);
                backtracking(k+1,sb,res,s.substring(i+1));
                //撤销选择
                sb.delete(sb.length()-part.length(),sb.length());
            }
        }
    }
}

 参考链接:https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3%20-%20%E6%90%9C%E7%B4%A2.md

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值