leetcode | 分类整理3(搜索)

依然范特西╮ 提交于 2020-02-11 17:10:53

搜索

BFS:广度优先

每一层遍历的节点都与根节点距离相同。设 di 表示第 i 个节点与根节点的距离,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di <= dj。利用这个结论,可以求解最短路径等 最优解 问题:第一次遍历到目的节点,其所经过的路径为最短路径。应该注意的是,使用 BFS 只能求解无权图的最短路径。

在程序实现 BFS 时需要考虑以下问题:

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

1) 每一层遍历的节点都与根节点距离相同:决定了什么时候path++:就是每遍历一层之前保留queue.size,这个size就是这一层的大小

2) 什么时候置flag?已经从队列中poll了之后

3) 最优解问题:因为每次遍历的都是和根节点距离相同的节点,所以最先到达终点的一定是最短路径

 

1091. 二进制矩阵中的最短路径(m)(经典BFS+visited)

重点:注意只有{0}一个数的情况

public int shortestPathBinaryMatrix(int[][] grid) {
        if(grid[0][0]==1 || grid[grid.length-1][grid[0].length-1]==1)
            return -1;
        if (grid.length == 1 && grid[0][0] == 0) {
            return 1;
        }
//        顺时针
        int[][] xOy = {{-1,0},{-1,-1},{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1}};
//        int[][] xOy = {{1, 0}, {1, 1}, {1,-1}, {0, 1}, {0, -1}, {-1, 0},{-1, -1}, {-1, 1}};
        int n = grid.length;
        boolean[][] visited = new boolean[n][n];
        visited[0][0] = true;

        Queue<int[]> queue = new LinkedList<>();
        queue.add(new int[] {0,0});
        int pathLength = 0;

        while (!queue.isEmpty()) {
//            这一层节点的个数
            int size = queue.size();
            pathLength++;
            while (size-- > 0) {
                int[] node = queue.poll();
                int x = node[0];
                int y = node[1];
//                grid[x][y] = 1;
                for (int[] d : xOy) {
                    int nx = x + d[0];
                    int ny = y + d[1];

                    if (nx == n-1 && ny == n-1) {
                        return pathLength+1;
                    }

                    if (nx < 0 || nx >= n || ny < 0 || ny >= n || visited[nx][ny] || grid[nx][ny] == 1){
                        continue;
                    }

                    visited[nx][ny] = true;
                    queue.add(new int[] {nx,ny});
                }
            }
        }

        return -1;
    }

 

 

279. 完全平方数(m)

思路:

可以将每个整数看成图中的一个节点,如果两个整数之差为一个平方数,那么这两个整数所在的节点就有一条边。

要求解最小的平方数数量,就是求解从节点 n 到节点 0 的最短路径。

本题也可以用动态规划求解,在之后动态规划部分中会再次出现。

完全平方数的规律:1,4,9,16.。。中间刚好相差(3,5,7,9....),即n^2-(n-1)^2 = 2n-1

public int numSquares(int n) {
        List<Integer> list = generateSquares(n);
        Queue<Integer> queue = new LinkedList<>();
        boolean[] visited = new boolean[n+1];

        queue.add(n);
        visited[n] = true;

        int level = 0;

        while (!queue.isEmpty()) {
            int size = queue.size();
            level++;
            while (size-- > 0) {
                int cur = queue.poll();
                for (int i: list
                     ) {
                    int next = cur - i;
//                    因为LinkedList是按顺序读,我们存的时候就是(1,4,9...)
                    if (next < 0) {
                        break;
                    }
                    if (next == 0) {
                        return level;
                    }

                    visited[next] = true;
                    queue.add(next);

                }
            }
        }

        return n;
    }

    /**
     * 生成小于 n 的平方数序列
     * @return 1,4,9,...
     */
    private List<Integer> generateSquares(int n) {
        List<Integer> squares = new ArrayList<>();
        int square = 1;
        int diff = 3;
        while (square <= n) {
            squares.add(square);
            square += diff;
            diff += 2;
        }
        return squares;
    }

 

127. 单词接龙(m)(重点!!)

重点:

1. 如何比较两个String中是不是有一位字符不同?

所以需要建立一个图(graph),单词是节点,如果两个单词之间只有一位不同则有一条边

2. 如何做visited[]标记数组?因为wordList中是String类型,如何按下标记录标记?

建立graph的时候使用下标而不是单词作为节点

public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        if (!wordList.contains(endWord)) {
            return 0;
        }

//        便于建图
        wordList.add(beginWord);

//        Queue<String> queue = new LinkedList<>();
        Queue<Integer> queue = new LinkedList<>();

        List<Integer>[] graph = buildGraph(wordList);

        int n = wordList.size();

        boolean[] visited = new boolean[n+1];
        int end = 0;
        for (int i = 0 ; i < wordList.size() ;i ++) {
            if (!endWord.equals(wordList.get(i))) {
                end++;
            }
            else {
                break;
            }
        }

        queue.add(n-1);
        visited[n-1] = true;

        int step = 0;

        while (!queue.isEmpty()) {
            int size = queue.size();
            step++;
            while (size-- > 0) {
                int cur = queue.poll();
//                graph[cur]是个list<Integer>
                for (int next : graph[cur])  {
                    if (next == end) {
                        return step+1;
                    }
                    if (visited[next]) {
                        continue;
                    }
                    visited[next] = true;
                    queue.add(next);
                }
            }
        }

        return 0;
    }

    public boolean isConnected(String s1,String s2) {
        int diffCnt = 0;
        for (int i = 0; i < s1.length() && diffCnt <= 1; i++) {
            if (s1.charAt(i) != s2.charAt(i)) {
                diffCnt++;
            }
        }

        return diffCnt == 1;
    }

    public List<Integer>[] buildGraph(List<String> wordList) {
        int n = wordList.size();
        List<Integer>[] graph = new List[n];
        for (int i = 0 ; i < n  ; i++) {
            graph[i] = new LinkedList<>();
            for (int j = 0 ; j < n; j++) {
                if (isConnected(wordList.get(i),wordList.get(j))) {
                    graph[i].add(j);
                }
            }
        }

        return graph;
    }

 

 

DFS(深度优先) 

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

在程序实现 DFS 时需要考虑以下问题:

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

 

695. 岛屿的最大面积(m)

重点为了方便起见,本题没有采用visited数组,而是直接将遍历过的置为0。注意找到什么情况需要恢复visited数组,什么情况不需要(本题不需要,剑指offer66题机器人路径规划也不需要),像剑指offer中65题找矩阵中的路径就需要(因为是要找序列)

int[][] direction = {{0,1},{0,-1},{1,0},{-1,0}};

    public int maxAreaOfIsland(int[][] grid) {
        if (grid == null || grid.length <= 0) {
            return 0;
        }
//        几行
        int m = grid.length;
//        几列
        int n = grid[0].length;
        int maxArea = 0;
        for (int i = 0 ; i < m ; i++) {
            for (int j = 0 ; j < n; j++) {
                maxArea = Math.max(maxArea,dfs(grid,i,j));
            }
        }
        return maxArea;
    }

    private int dfs(int[][] grid, int i, int j) {
        if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] == 0) {
            return 0;
        }
//        本来应该是visited数组来标记,但是这里为了方便直接将访问过的置为0
        grid[i][j] = 0;
        int area = 1;
        for (int[] d : direction) {
            area += dfs(grid,i+d[0],j+d[1]);
        }
        return area;
    }

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!