演算法~廣度優先搜尋(Breadth First Search)一石激起幹層浪(附帶6道練習題)
廣度優先搜尋模型
BFS() {
1.建立起始步驟,佇列初始化
2.遍歷佇列中的每一種可能,whlie(佇列不為空)
通過隊頭元素帶出下一步的所有可能,並且依次入隊
判斷當前情況是否達成目標:按照目標要求處理邏輯
繼續遍歷佇列中的剩餘情況
}
出迷宮
假設有一個迷宮,裡面有障礙物,迷宮用二維矩陣表示,標記為O的地方表示可以通過,標記為1的地方表示障礙物,不能通過。現在給一個迷宮出口,讓你判斷是否可以從入口進來之後,走出迷宮,每次可以向任意方向走。
假設是一個10*10的迷宮,入口在(1,1)的位置,出口在(8,10)的位置,通過(1,1)一步可以走到的位置有兩個(1,2),(2,1)·但是這兩個點並不是出口,需要繼續通過這兩個位置進一步搜尋,假設現在在(1,2),下一次一步可以到達的新的位置為(1,3),(2.2)。而通過(2,1)可以一步到達的新的位置為(2,2),(3,1),但是這裡(2,2)是重複的,所以每一個點在走的過程中需要標記是否已經走過了。
兩步之後,還沒沒有走到出口,這時候需要通過新加入的點再去探索下一步能走到哪些新的點上,重複這個過程,直到走到出口為止。
- 程式碼
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
/**
* Created with IntelliJ IDEA.
* Description: If you don't work hard, you will a loser.
* User: Listen-Y.
* Date: 2020-09-22
* Time: 21:34
*/
public class SolutionBreadth {
static class Node {
public int x;
public int y;
public Node(int x, int y) {
this.x = x;
this.y = y;
}
}
/**
* 迷宮問題
*/
public static void main(String[] args) {
int sr, sc, endR, endC;
Scanner scanner = new Scanner(System.in);
System.out.print("輸入迷宮起點與終點:");
sr = scanner.nextByte();
sc = scanner.nextByte();
endR = scanner.nextByte();
endC = scanner.nextByte();
int[][] gird = {{0,1,0,0},
{0,0,0,1},
{0,1,0,0},
{0,0,1,0}};
System.out.println(DFS(gird, sr, sc, endR, endC));
}
private static int[][] next = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
private static boolean DFS(int[][] gird, int sr, int sc, int endR, int endC) {
int row = gird.length;
if (row == 0) {
return false;
}
int col = gird[0].length;
//需要一個used儲存訪問過得點
boolean[][] used = new boolean[row][col];
//需要一個佇列儲存需要遍歷的點
Queue<Node> queue = new LinkedList<>();
queue.offer(new Node(sr, sc));
//標記起點已被使用
used[sr][sc] = true;
//只要佇列不為空就說明還有機會到達終點
while (!queue.isEmpty()) {
//檢視起點的四周, 看哪個方向可以走
for (int i = 0; i < 4; i++) {
int newX = queue.peek().x + next[i][0];
int newY = queue.peek().y + next[i][1];
//判斷邊界
if (newX < 0 || newX >= row || newY < 0 || newY >= col) {
continue;
}
//如果此時位置無障礙, 並且未被訪問就入佇列
if (gird[newX][newY] == 0 && !used[newX][newY]) {
queue.offer(new Node(newX, newY));
//並標記這點被訪問過
used[newX][newY] = true;
}
//如果此時已經是終點就結束方法
if (newX == endR && newY == endC) {
return true;
}
}
//否則就出佇列判斷下一個點
queue.poll();
}
return false;
}
}
員工的重要性
- 題目描述
給定一個儲存員工資訊的資料結構,它包含了員工唯一的id,重要度 和 直系下屬的id。
比如,員工1是員工2的領導,員工2是員工3的領導。他們相應的重要度為15, 10, 5。那麼員工1的資料結構是[1, 15, [2]],員工2的資料結構是[2, 10, [3]],員工3的資料結構是[3, 5, []]。注意雖然員工3也是員工1的一個下屬,但是由於並不是直系下屬,因此沒有體現在員工1的資料結構中。
現在輸入一個公司的所有員工資訊,以及單個員工id,返回這個員工和他所有下屬的重要度之和。
示例 1:
輸入: [[1, 5, [2, 3]], [2, 3, []], [3, 3, []]], 1
輸出: 11
解釋:
員工1自身的重要度是5,他有兩個直系下屬2和3,而且2和3的重要度均為3。因此員工1的總重要度是 5 + 3 + 3 = 11。
來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/employee-importance
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。
- 程式碼
/*
// Definition for Employee.
class Employee {
public int id;
public int importance;
public List<Integer> subordinates;
};
*/
class Solution {
public int getImportance(List<Employee> employees, int id) {
//將所有員工儲存在map中
Map<Integer, Employee> map = new HashMap<>();
for (Employee em : employees) {
map.put(em.id, em);
}
//需要返回的重要性
int importance = 0;
//建立一個佇列儲存下屬員工
Queue<Employee> queue = new LinkedList<>();
queue.offer(map.get(id));
while (!queue.isEmpty()) {
//獲得當前有佇列裡有幾個下屬員工
int size = queue.size();
while (size-- > 0) {
//獲取下屬
Employee employ = queue.poll();
importance += employ.importance;
//判斷當前員工還有沒有下屬 如果有就加到佇列中
if (employ.subordinates != null) {
for (int curId : employ.subordinates) {
queue.offer(map.get(curId));
}
}
}
}
return importance;
}
}
N叉樹的遍歷
- 題目描述
給定一個 N 叉樹,返回其節點值的層序遍歷。 (即從左到右,逐層遍歷)。
例如,給定一個 3叉樹 :
返回其層序遍歷:
[
[1],
[3,2,4],
[5,6]
]
來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。
- 程式碼
/*
// Definition for a Node.
class Node {
public int val;
public List<Node> children;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, List<Node> _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public List<List<Integer>> levelOrder(Node root) {
//建立一個佇列儲存每一層的節點資料
Queue<Node> queue = new LinkedList<>();
queue.offer(root);
List<List<Integer>> ret = new ArrayList<>();
if (root == null) return ret;
while (!queue.isEmpty()) {
//獲取當前層的結點個數
int size = queue.size();
List<Integer> list = new ArrayList<>();
while (size-- > 0) {
//將該層所有結點的資料儲存在連結串列中
Node cur = queue.poll();
list.add(cur.val);
//如果當前結點下一層還有結點就入佇列
if (cur.children != null) {
for (Node node : cur.children) {
queue.offer(node);
}
}
}
ret.add(list);
}
return ret;
}
}
腐爛的橘子
- 題目描述
在給定的網格中,每個單元格可以有以下三個值之一:
值 0 代表空單元格;
值 1 代表新鮮橘子;
值 2 代表腐爛的橘子。
每分鐘,任何與腐爛的橘子(在 4 個正方向上)相鄰的新鮮橘子都會腐爛。
返回直到單元格中沒有新鮮橘子為止所必須經過的最小分鐘數。如果不可能,返回 -1。
來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/rotting-oranges
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。
- 程式碼
class Solution {
static class Node {
public int x;
public int y;
public Node(int x, int y) {
this.x = x;
this.y = y;
}
}
private int[][] next = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
public int orangesRotting(int[][] grid) {
int row = grid.length;
if (row == 0) return 0;
int col = grid[0].length;
//需要一個佇列儲存壞的橘子
Queue<Node> queue = new LinkedList<>();
//遍歷找到壞的橘子
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (grid[i][j] == 2) {
queue.offer(new Node(i, j));
}
}
}
int time = 0;
//如果此時有壞的橘子就進行處理感染
while (!queue.isEmpty()) {
//用一個資料判斷是否進行了感染
boolean ok = false;
int size = queue.size();
//進行一分鐘的感染
while (size-- > 0) {
Node cur = queue.poll();
//遍歷該橘子的四周
for (int i = 0; i < 4; i++) {
int newR = cur.x + next[i][0];
int newC = cur.y + next[i][1];
//判斷邊界
if (newR < 0 || newR >= row || newC < 0 || newC >= col) {
continue;
}
//判斷如果當前是新鮮橘子就進行感染
if (grid[newR][newC] == 1) {
grid[newR][newC] = 2;
ok = true;
//並將感染後的橘子放到佇列中
queue.offer(new Node(newR, newC));
}
}
}
//如果有被感染
if (ok) {
time++;
}
}
//檢查如果還有沒被感染的橘子就返回-1
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (grid[i][j] == 1) {
return -1;
}
}
}
return time;
}
}
單詞接龍
- 題目描述
給定兩個單詞(beginWord 和 endWord)和一個字典,找到從 beginWord 到 endWord 的最短轉換序列的長度。轉換需遵循如下規則:
每次轉換隻能改變一個字母。
轉換過程中的中間單詞必須是字典中的單詞。
說明:
如果不存在這樣的轉換序列,返回 0。
所有單詞具有相同的長度。
所有單詞只由小寫字母組成。
字典中不存在重複的單詞。
你可以假設 beginWord 和 endWord 是非空的,且二者不相同。
來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/word-ladder
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。
- 程式碼
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
//將所有單詞放到hash表中 便於查詢
Set<String> dict = new HashSet<>();
for (String word : wordList) {
dict.add(word);
}
//判斷此時字典是否有這個endWord
if (!dict.contains(endWord)) {
return 0;
}
//使用一個set去儲存訪問過的單詞
Set<String> used = new HashSet<>();
used.add(beginWord);
//使用一個佇列去儲存替換一次的單詞
Queue<String> queue = new LinkedList<>();
queue.offer(beginWord);
//計數器
int step = 1;
while (!queue.isEmpty()) {
//取出一次變化後的說有單詞
int size = queue.size();
while (size-- > 0) {
String curWord = queue.poll();
//對這個單詞進行每一個位置的每一個字元的替換
for (int i = 0; i < curWord.length(); i++) {
StringBuilder builder = new StringBuilder(curWord);
for (char ch = 'a'; ch <= 'z'; ch++) {
builder.setCharAt(i, ch);
String newWord = builder.toString();
//如果此時就是endWord就結束
if (newWord.equals(endWord)) {
return step + 1;
}
//判斷這個新單詞是否在字典中 沒有被訪問過
if (dict.contains(newWord) && !used.contains(newWord)) {
//否則入佇列
queue.offer(newWord);
used.add(newWord);
}
}
}
}
//完成所有size表示進行了一次改變
step++;
}
//如果到這還沒有返回說明不能扎到
return 0;
}
}
開啟轉盤鎖
- 題目描述
你有一個帶有四個圓形撥輪的轉盤鎖。每個撥輪都有10個數字: ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ 。每個撥輪可以自由旋轉:例如把 ‘9’ 變為 ‘0’,‘0’ 變為 ‘9’ 。每次旋轉都只能旋轉一個撥輪的一位數字。
鎖的初始數字為 ‘0000’ ,一個代表四個撥輪的數字的字串。
列表 deadends 包含了一組死亡數字,一旦撥輪的數字和列表裡的任何一個元素相同,這個鎖將會被永久鎖定,無法再被旋轉。
字串 target 代表可以解鎖的數字,你需要給出最小的旋轉次數,如果無論如何不能解鎖,返回 -1。
來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/open-the-lock
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。
- 程式碼
class Solution {
public int openLock(String[] deadends, String target) {
//用一個hash去儲存dead便於查詢
Set<String> deadSet = new HashSet<>();
for (String str : deadends) {
deadSet.add(str);
}
//如果0000是死亡數字那麼永遠有達不到
if (deadSet.contains("0000")) {
return -1;
}
//如果當前就是0000 就返回0
if (target.equals("0000")) {
return 0;
}
//用一個佇列去儲存此時的轉盤上的數字
Queue<String> queue = new LinkedList<>();
queue.offer("0000");
//計數器
int step = 0;
//用一個set儲存訪問過得密碼
Set<String> used = new HashSet<>();
while (!queue.isEmpty()) {
int size = queue.size();
while (size-- > 0) {
String curStr = queue.poll();
//進行一次的撥盤
for (int i = 0; i < 4; i++) {
//一次波動有倆種可能
char newOne;
char newTwo;
//對curStr當前的字元是0/9要進行特殊處理
if (curStr.charAt(i) == '0' || curStr.charAt(i) == '9') {
if (curStr.charAt(i) == '0') {
newOne = '1';
newTwo = '9';
} else {
newOne = '0';
newTwo = '8';
}
} else {
newOne = (char) (curStr.charAt(i) + 1);
newTwo = (char) (curStr.charAt(i) - 1);
}
//將撥好的單個數字進行與其他三個字元想組合
StringBuilder oneBuilder = new StringBuilder(curStr);
StringBuilder twoBuilder = new StringBuilder(curStr);
oneBuilder.setCharAt(i, newOne);
twoBuilder.setCharAt(i, newTwo);
String oneStr = oneBuilder.toString();
String twoStr = twoBuilder.toString();
//對此時這倆個字串進行判斷
// 如果已經是target就返回操作步數
if (oneStr.equals(target) || twoStr.equals(target)) {
return step + 1;
}
//分別對倆個字串進行判斷是否為鎖死密碼和是否已經遍歷過
if (!deadSet.contains(oneStr) && !used.contains(oneStr)) {
queue.offer(oneStr);
used.add(oneStr);
}
if (!deadSet.contains(twoStr) && !used.contains(twoStr)) {
queue.offer(twoStr);
used.add(twoStr);
}
}
}
step++;
}
return -1;
}
}
相關文章
- 【知識點】深度優先搜尋 Depth First Search
- 深度優先搜尋 (Depth First Search 簡稱:DFS)
- 小白的深度優先搜尋(Depth First Search)學習日記(Python)Python
- 基本演算法——深度優先搜尋(DFS)和廣度優先搜尋(BFS)演算法
- 【演算法】廣度/寬度優先搜尋(BFS)演算法
- bfs廣度優先搜尋
- ybtoj:廣度優先搜尋
- 深度和廣度優先搜尋演算法演算法
- 演算法筆記(廣度優先搜尋)演算法筆記
- 演算法競賽——BFS廣度優先搜尋演算法
- 圖的遍歷:深度優先搜尋與廣度優先搜尋
- c++ 廣度優先搜尋(寬搜)C++
- LeetCode演算法練習——深度優先搜尋 DFSLeetCode演算法
- python 二叉樹深度優先搜尋和廣度優先搜尋Python二叉樹
- 圖的廣度優先搜尋和深度優先搜尋Python實現Python
- 廣度優先搜尋(BFS)思路及演算法分析演算法
- 演算法(三):圖解廣度優先搜尋演算法演算法圖解
- js版本的(廣、深)度優先搜尋JS
- golang學習筆記——迷宮的廣度優先搜尋Golang筆記
- 【程式碼隨想錄】廣度優先搜尋
- 啟發式搜尋的方式(深度優先,廣度優先)和 搜尋方法(Dijkstra‘s演算法,代價一致搜尋,貪心搜尋 ,A星搜尋)演算法
- 0演算法基礎學演算法 搜尋篇第二講 BFS廣度優先搜尋的思想演算法
- 「Golang成長之路」迷宮的廣度優先搜尋Golang
- 廣度優先搜尋相關面試演算法總結(非圖論方面)面試演算法圖論
- 【演算法】深度優先搜尋(DFS)演算法
- 深度DFS 和 廣度BFS搜尋演算法學習演算法
- Android程式設計師面試會遇到的演算法(part 2 廣度優先搜尋)Android程式設計師面試演算法
- 深度優先搜尋
- leetcode 刷題之深度優先搜尋LeetCode
- POJ1915,雙向寬度優先搜尋
- 深度優先搜尋演算法(DFS)講解演算法
- 深度優先搜尋演算法-dfs講解演算法
- DFS(深度優先搜尋)
- ybtoj:深度優先搜尋
- 深度優先與廣度優先
- 十、深度優先 && 廣度優先
- 深度優先搜尋(DFS)思路及演算法分析演算法
- 0基礎學演算法 搜尋篇第一講 深度優先搜尋演算法