leetcode高頻題筆記之DFS和BFS

代碼模板

BFS模板

def BFS(graph, start, end):
    visited = set()
	queue = [] 
	queue.append([start]) 

	while queue: 
		node = queue.pop() 
		visited.add(node)

		process(node) 
		nodes = generate_related_nodes(node) 
		queue.push(nodes)

	# other processing work 
	...

DFS模板

遞歸玩法

visited = set() 

def dfs(node, visited):
    if node in visited: # terminator
    	# already visited 
    	return 

	visited.add(node) 

	# process current node here. 
	...
	for next_node in node.children(): 
		if next_node not in visited: 
			dfs(next_node, visited)

非遞歸玩法

def DFS(self, tree): 

	if tree.root is None: 
		return [] 

	visited, stack = [], [tree.root]

	while stack: 
		node = stack.pop() 
		visited.add(node)

		process (node) 
		nodes = generate_related_nodes(node) 
		stack.push(nodes) 

	# other processing work 
	...

二叉樹的層次遍歷

在這裏插入圖片描述
BFS的高頻經典題目

BFS解法

class Solution {
     public List<List<Integer>> levelOrder(TreeNode root) {
        if (root == null) return new ArrayList<>();
        LinkedList<TreeNode> queue = new LinkedList<>();
        List<List<Integer>> res = new ArrayList<>();
        queue.offerLast(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            List<Integer> tmpList = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                TreeNode cur = queue.pollFirst();
                tmpList.add(cur.val);
                if (cur.left != null) queue.offerLast(cur.left);
                if (cur.right != null) queue.offerLast(cur.right);
            }
            res.add(tmpList);
        }
        return res;
    }
}

雖然是經典的BFS題目,但是用DFS也可以做出來

DFS解法

public class Solution {
    List<List<Integer>> res;
    public List<List<Integer>> levelOrder(TreeNode root) {
        if (root == null) return new LinkedList<>();
        res = new LinkedList<>();
        dfs(root, 0);
        return res;
    }
    private void dfs(TreeNode root, int depth) {
        if (root == null) return;
        if (depth >= res.size()) res.add(new LinkedList<>());
        res.get(depth).add(root.val);
        dfs(root.left, depth + 1);
        dfs(root.right, depth + 1);
    }
}

括號生成

在這裏插入圖片描述
DFS解法

public class Solution {

    List<String> res;
    int n;

    public List<String> generateParenthesis(int n) {
        res = new ArrayList<>();
        this.n = n;
        dfs(0, 0, "");
        return res;
    }

    /**
     * @param left  左括號個數
     * @param right 右括號個數
     * @param s     結果字符串
     */
    private void dfs(int left, int right, String s) {
        //退出條件
        if (left == n && right == n) {
            res.add(s);
            return;
        }

        //可以生成左括號的條件:左括號小於n
        if (left < n) dfs(left + 1, right, s + "(");
        //可以生成右括號的條件:右括號小於左括號
        if (left > right) dfs(left, right + 1, s + ")");

        //reserve
    }
}

在每個樹行中找最大值

在這裏插入圖片描述
和二叉樹的中序遍歷做法幾乎一模一樣,只需要修改結果集產生的方式

BFS解法

public class Solution {
    public List<Integer> largestValues(TreeNode root) {
        if (root == null) return new ArrayList<>();
        List<Integer> res = new ArrayList<>();
        Deque<TreeNode> deque = new LinkedList<TreeNode>();
        deque.offerLast(root);
        while (!deque.isEmpty()) {
            int size = deque.size();
            int lineMax = Integer.MIN_VALUE;
            for (int i = 0; i < size; i++) {
                TreeNode cur = deque.pollFirst();
                lineMax = Math.max(lineMax, cur.val);
                if (cur.left != null) deque.offerLast(cur.left);
                if (cur.right != null) deque.offerLast(cur.right);
            }
            res.add(lineMax);
        }
        return res;
    }
}

島嶼數量

在這裏插入圖片描述
DFS解法

遍歷二維數組,當發現1之後,計數+1,然後調用函數dfs遞歸的去將這個1的所有鄰接的1都變爲0,然後繼續搜索

public class Solution {
    public int numIslands(char[][] grid) {

        if (grid == null || grid.length == 0 || grid[0].length == 0) return 0;

        int count = 0;
        int rows = grid.length;
        int cols = grid[0].length;
        for (int r = 0; r < rows; r++) {
            for (int c = 0; c < cols; c++) {
                if (grid[r][c] == '1') {
                    count++;
                    //發現島嶼,然後廣度優先搜索臨界的區域,全部變成海洋
                    dfs(r, c, rows, cols, grid);
                }
            }
        }
        return count;
    }

    private void dfs(int r, int c, int rows, int cols, char[][] grid) {
        if (r == -1 || c == -1 || r == rows || c == cols || grid[r][c] != '1') return;

        //將當前1標記爲0
        grid[r][c] = 0;

        //向上左右進行擴展
        dfs(r + 1, c, rows, cols, grid);
        dfs(r - 1, c, rows, cols, grid);
        dfs(r, c + 1, rows, cols, grid);
        dfs(r, c - 1, rows, cols, grid);
    }

}

BFS解法

public class Solution {
    public int numIslands(char[][] grid) {

        if (grid == null || grid.length == 0 || grid[0].length == 0) return 0;

        int count = 0;
        int rows = grid.length;
        int cols = grid[0].length;
        for (int r = 0; r < rows; r++) {
            for (int c = 0; c < cols; c++) {
                if (grid[r][c] == '1') {
                    count++;
                    //發現島嶼,然後廣度優先搜索臨界的區域,全部變成海洋
                    bfs(r, c, rows, cols, grid);
                }
            }
        }
        return count;
    }

    private void bfs(int r, int c, int rows, int cols, char[][] grid) {
        Deque<Integer> deque = new LinkedList<>();
        //將二維數組換爲一維進行存儲
        deque.offer(r * cols + c);
        while (!deque.isEmpty()) {
            Integer cur = deque.poll();
            int row = cur / cols;
            int col = cur % cols;
            //如果已經遍歷過了,就不再重複
            if (grid[row][col] == '0') {
                continue;
            }
            grid[row][col] = '0';

            if (row != (rows - 1) && grid[row + 1][col] == '1') {
                deque.offer((row + 1) * cols + col);
            }

            if (col != (cols - 1) && grid[row][col + 1] == '1') {
                deque.offer(row * cols + col + 1);
            }

            if (row != 0 && grid[row - 1][col] == '1') {
                deque.offer((row - 1) * cols + col);
            }

            if (col != 0 && grid[row][col - 1] == '1') {
                deque.offer(row * cols + col - 1);
            }

        }

    }

}

島嶼的最大面積

在這裏插入圖片描述
解決方案和上一個題島嶼數量一致,遍歷整個數組,發現1之後,採用dfs或者bfs進行沉島

DFS解法

public class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) return 0;
        int maxSize = 0;
        int rows = grid.length;
        int cols = grid[0].length;
        for (int r = 0; r < rows; r++) {
            for (int c = 0; c < cols; c++) {
                if (grid[r][c] == 1) {
                    maxSize = Math.max(maxSize, dfs(r, c, rows, cols, grid));
                }
            }
        }
        return maxSize;
    }

    private Integer dfs(int r, int c, int rows, int cols, int[][] grid) {

        if (r == -1 || c == -1 || r == rows || c == cols || grid[r][c] == 0) {
            return 0;
        }
        //將訪問過的島嶼弄成0
        grid[r][c] = 0;

        int num = 1;

        //每個方向都進行遍歷,遍歷的結果進行累加就是最終的面積
        num += dfs(r + 1, c, rows, cols, grid);
        num += dfs(r, c + 1, rows, cols, grid);
        num += dfs(r - 1, c, rows, cols, grid);
        num += dfs(r, c - 1, rows, cols, grid);

        return num;
    }
}

被圍繞的區域

在這裏插入圖片描述
思路:

  • 遍歷查找邊界‘O’
  • 讓邊界’O’採用搜索算法(bfs or dfs)查找鄰接‘O‘
  • 將邊界‘O’和他們的鄰接‘O’都設置成‘#’
  • 剩下的‘O’就都是圍繞的了
  • 遍歷數組,將所有的‘O‘修改成‘X’,將所有的‘#’修改成‘O‘

DFS解法

public class Solution {
    public void solve(char[][] board) {
        if (board == null || board.length == 0 || board[0].length == 0) return;
        int m = board.length;
        int n = board[0].length;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                boolean isEdge = i == 0 || j == 0 || i == m - 1 || j == n - 1;
                if (isEdge && board[i][j] == 'O')
                    dfs(board, i, j);
            }
        }
        //將與邊界'O'有段的'O'標記之後
        //將‘O’轉換爲‘X’
        //將‘#’轉換爲‘O’
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (board[i][j] == 'O') board[i][j] = 'X';
                if (board[i][j] == '#') board[i][j] = 'O';
            }
        }
    }

    private void dfs(char[][] board, int i, int j) {
        if (i == -1 || j == -1
                || i == board.length || j == board[0].length
                || board[i][j] == 'X' || board[i][j] == '#') {
            return;
        }
        //將邊界‘O’和與邊界‘O’相鄰的‘O’都設置爲#
        board[i][j] = '#';
        dfs(board, i - 1, j);
        dfs(board, i + 1, j);
        dfs(board, i, j - 1);
        dfs(board, i, j + 1);
    }
}

單詞接龍

在這裏插入圖片描述
BFS解法1

用一個boolean數組記錄字典中的字符串是否被訪問過了
標準的bfs遍歷模板(和二叉樹的層次遍歷相似),只是加上了一些條件的判斷

  • 如果beginWord在字典中,先標記爲訪問過,不需要再訪問
  • 如果字符串被訪問過了,就不再進行訪問
  • 如果訪問的字符串不滿足和當前串只差一個字符不同,也不再進行操作
  • 當匹配上了endWord就直接返回(因爲是層序遍歷,每層的次數相同且由少到多,所以第一次發現一定是最短的)
public class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        if (!wordList.contains(endWord)) {
            return 0;
        }
        //記錄是否訪問過
        boolean[] visited = new boolean[wordList.size()];
        //檢驗是否存在beginWord,如果存在,就置爲訪問過了,沒必要訪問
        int index = wordList.indexOf(beginWord);
        if (index != -1) {
            visited[index] = true;
        }
        //bfs暫存隊列
        Queue<String> queue = new LinkedList<>();
        queue.offer(beginWord);
        int count = 0;
        while (!queue.isEmpty()) {
            int size = queue.size();
            count++;
            while (size-- > 0) {
                String start = queue.poll();
                for (int i = 0; i < wordList.size(); i++) {
                    //訪問過了,跳過,訪問下一個
                    if (visited[i]) continue;
                    String s = wordList.get(i);
                    //不滿足和當前只差一個字符不同,跳過,訪問下一個
                    if (!canConvert(start, s)) {
                        continue;
                    }
                    //和endWord匹配上了,進行返回,因爲是bfs,所以找到了直接返回就是最短的
                    if (s.equals(endWord)) {
                        return count + 1;
                    }
                    //訪問完了將當前置爲已訪問
                    visited[i] = true;
                    //當前值壓棧
                    queue.offer(s);
                }
            }
        }
        return 0;

    }
    //判斷s1和s2是否只有一個字符不同
    private boolean canConvert(String s1, String s2) {
        int count = 0;
        for (int i = 0; i < s1.length(); i++) {
            if (s1.charAt(i) != s2.charAt(i)) {
                count++;
                if (count > 1) {//達到兩個不同了,說明不符合
                    return false;
                }
            }
        }
        //不同字符小於兩個,判斷是否是一個
        return count == 1;
    }
}

BFS解法2:雙向BFS

public class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        int end = wordList.indexOf(endWord);
        if (end == -1) return 0;
        wordList.add(beginWord);
        int start = wordList.size() - 1;

        //bfs暫存隊列
        Queue<Integer> queue1 = new LinkedList<>();
        Queue<Integer> queue2 = new LinkedList<>();
        //訪問記錄set
        Set<Integer> visited1 = new HashSet<>();
        Set<Integer> visited2 = new HashSet<>();
        queue1.offer(start);
        queue2.offer(end);
        visited1.add(start);
        visited2.add(end);
        int count = 0;
        while (!queue1.isEmpty() && !queue2.isEmpty()) {
            count++;
            //判斷兩個隊列的長度,把短的令成1,然後把短的進行遍歷,加快速度
            if (queue1.size() > queue2.size()) {//將兩個的隊列和set都交換下
                Queue<Integer> tmpqueue = queue1;
                queue1 = queue2;
                queue2 = tmpqueue;
                Set<Integer> tmpset = visited1;
                visited1 = visited2;
                visited2 = tmpset;
            }
            int size = queue1.size();

            while (size-- > 0) {
                String s = wordList.get(queue1.poll());
                for (int i = 0; i < wordList.size(); i++) {
                    //訪問過了,跳過,訪問下一個
                    if (visited1.contains(i)) continue;

                    //不滿足和當前只差一個字符不同,跳過,訪問下一個
                    if (!canConvert(s, wordList.get(i))) {
                        continue;
                    }
                    //退出條件
                    if (visited2.contains(i)) {
                        return count + 1;
                    }
                    //訪問完了將當前置爲已訪問
                    visited1.add(i);
                    //當前值壓棧
                    queue1.offer(i);
                }
            }
        }
        return 0;

    }

    //判斷s1和s2是否只有一個字符不同
    private boolean canConvert(String s1, String s2) {
        int count = 0;
        for (int i = 0; i < s1.length(); i++) {
            if (s1.charAt(i) != s2.charAt(i)) {
                if (++count > 1) {//達到兩個不同了,說明不符合
                    return false;
                }
            }
        }
        //不同字符小於兩個,判斷是否是一個
        return count == 1;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章