Java超詳細(xì)精講數(shù)據(jù)結(jié)構(gòu)之bfs與雙端隊(duì)列
一.bfs
bfs(廣度優(yōu)先搜索),類(lèi)似二叉樹(shù)的層序遍歷,利用隊(duì)列完成。一般用于求最短路。
圖的最短路問(wèn)題:
給定一個(gè)無(wú)向圖,每條邊的長(zhǎng)度都是1。求1號(hào)點(diǎn)到x號(hào)點(diǎn)的最短距離。 頂點(diǎn)數(shù)n 邊數(shù)為m
q次詢問(wèn) 輸入x 輸出1到x的最短距離。 若1號(hào)點(diǎn)到x不連通,則輸出-1
二.雙端隊(duì)列
雙端隊(duì)列的應(yīng)用(區(qū)間翻轉(zhuǎn)):
對(duì)于長(zhǎng)度為n的數(shù)組,給定一個(gè)長(zhǎng)度為m的區(qū)間,區(qū)間初始位置為a[1]到a[m]。
3種操作:
- 區(qū)間右移(最右端不會(huì)超過(guò)a[n])
- 區(qū)間左移(最左端不會(huì)超過(guò)a[n])
- 區(qū)間內(nèi)所有數(shù)翻轉(zhuǎn)。
q次操作后請(qǐng)你還原數(shù)組。
三.算法題
1.kotori和迷宮
難度??
知識(shí)點(diǎn):bfs
首先找到k字母,然后從k字母位置開(kāi)始bfs。bfs過(guò)程中即可得到k到每個(gè)e的最短路程。(要注意走過(guò)的e不可繼續(xù)往下走)
題目描述:
kotori在一個(gè)n*m迷宮里,迷宮的最外層被巖漿淹沒(méi),無(wú)法涉足,迷宮內(nèi)有k個(gè)出口。kotori只能上下左右四個(gè)方向移動(dòng)。她想知道有多少出口是她能到達(dá)的,最近的出口離她有多遠(yuǎn)?
輸入描述:
第一行為兩個(gè)整數(shù)n和m,代表迷宮的行和列數(shù) (1≤n,m≤30)
后面緊跟著n行長(zhǎng)度為m的字符串來(lái)描述迷宮。'k'代表kotori開(kāi)始的位置,'.'代表道路,'*'代表墻壁,'e'代表出口。保證輸入合法。
輸出描述:
若有出口可以抵達(dá),則輸出2個(gè)整數(shù),第一個(gè)代表kotori可選擇的出口的數(shù)量,第二個(gè)代表kotori到最近的出口的步數(shù)。(注意,kotori到達(dá)出口一定會(huì)離開(kāi)迷宮)
若沒(méi)有出口可以抵達(dá),則輸出-1。
示例1
輸入
6 8
e.*.*e.*
.**.*.*e
..*k**..
***.*.e*
.**.*.**
*......e
輸出
2 7
說(shuō)明
可供選擇坐標(biāo)為[4,7]和[6,8],到kotori的距離分別是8和7步。
import java.util.*; import java.io.*; public class Main{ public static void main(String[] args) throws IOException{ BufferedReader bf = new BufferedReader(new InputStreamReader(System.in)); String[] s1 = bf.readLine().split(" "); int n = Integer.parseInt(s1[0]); int m = Integer.parseInt(s1[1]); //建立地圖、標(biāo)記圖 char[][] maze = new char[n][m]; boolean[][] visited = new boolean[n][m]; //紀(jì)錄步數(shù) int[][] dis = new int[n][m]; //紀(jì)錄初始的坐標(biāo) int ki = 0, kj = 0; for(int i = 0; i < n; i++){ String s = bf.readLine(); for(int j = 0; j < m; j++){ dis[i][j] = Integer.MAX_VALUE; char c = s.charAt(j); maze[i][j] = c; if(c == 'k'){ ki = i; kj = j; } } } int count = 0, min = Integer.MAX_VALUE; Queue<Integer> queue = new ArrayDeque<>(); //二維數(shù)組的性質(zhì),保存了坐標(biāo),并且節(jié)省了空間 queue.add(ki * m + kj); visited[ki][kj] = true; dis[ki][kj]= 0; while(!queue.isEmpty()){ int temp = queue.poll(); int tempi = temp / m, tempj = temp % m; //支持八個(gè)方向的移動(dòng)或者不移動(dòng)(但是因?yàn)镸ath.abs(i - j) == 1限定了絕對(duì)值為1,所以變成了四個(gè)方向) for(int i = -1; i <= 1; i++){ for(int j = -1; j <= 1; j++){ if(Math.abs(i - j) == 1 && tempi + i >= 0 && tempi + i < n && tempj + j >= 0 && tempj + j < m && !visited[tempi + i][tempj + j]){ if(maze[tempi + i][tempj + j] == '.'){ visited[tempi + i][tempj + j] = true; dis[tempi + i][tempj + j] = dis[tempi][tempj] + 1; queue.add((tempi + i) * m + (tempj + j)); } if(maze[tempi + i][tempj + j] == 'e'){ visited[tempi + i][tempj + j] = true; dis[tempi + i][tempj + j] = dis[tempi][tempj] + 1; min = Math.min(min, dis[tempi][tempj] + 1); count++; } } } } } if(count == 0) System.out.print(-1); else System.out.print(count + " " + min); } }
思考:隊(duì)列是怎么實(shí)現(xiàn)bfs的?
1.起始點(diǎn)入隊(duì)-->2.將起始點(diǎn)四個(gè)方向的可達(dá)點(diǎn)入隊(duì)-->3.起始點(diǎn)出隊(duì)。以此循序依次訪問(wèn)隊(duì)列中的元素。
2.小紅找紅點(diǎn)
難度???
知識(shí)點(diǎn):bfs,多源最短路
多源最短路的求法:在bfs開(kāi)始之前將所有點(diǎn)都扔進(jìn)隊(duì)列,然后開(kāi)始bfs即可。
題目描述:
小紅拿到了一張無(wú)向圖,有 n個(gè)頂點(diǎn)和m條邊。每條邊的長(zhǎng)度為 1 。
小紅給一些頂點(diǎn)染成了紅色。她想知道,對(duì)于每個(gè)頂點(diǎn),到附近最近的紅色點(diǎn)的距離為多少?
輸入描述:
第一行輸出兩個(gè)正整數(shù) n 和 m ,用空格隔開(kāi)。分別代表頂點(diǎn)數(shù)和邊數(shù)。
第二行輸入一個(gè)長(zhǎng)度為 n 的字符串,代表每個(gè)頂點(diǎn)的染色情況。第i 個(gè)字符為 'R' 代表被染成紅色,為 'W' 代表未被染色。
接下來(lái)的m 行,每行兩個(gè)正整數(shù) x 和y ,代表x 和y 有一條無(wú)向邊相連。
不保證圖是整體連通的。不保證沒(méi)有重邊和自環(huán)。
1<=n,m<=10^5
輸出描述:
輸出一行 n 個(gè)整數(shù),代表從1 到 n 每個(gè)頂點(diǎn)到最近的紅色頂點(diǎn)的距離。若對(duì)于某點(diǎn)而言無(wú)論如何都走不到紅色頂點(diǎn),則輸出 -1 。
示例1:
輸入
5 5
RWWRW
1 2
3 3
1 2
2 5
1 4
輸出
0 1 -1 0 2
說(shuō)明
樣例的圖如上所示。
import java.util.*; import java.io.*; public class Main{ static ArrayList<Integer>[] g; static String[] strings; static int[] visited; static int[] dis; public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] firstLine = br.readLine().split(" "); int n = Integer.parseInt(firstLine[0]); int m = Integer.parseInt(firstLine[1]); g = new ArrayList[n+1]; visited = new int[n+1]; dis= new int[n+1]; for (int i=1;i<n+1;i++) { g[i] = new ArrayList<Integer>(); } //一個(gè)字符一個(gè)字符的讀取 strings = br.readLine().split(""); for (int i=0;i<m;i++) { //描繪雙向圖 String[] temp = br.readLine().split(" "); int x = Integer.parseInt(temp[0]); int y = Integer.parseInt(temp[1]); g[x].add(y); g[y].add(x); } //g[x]代表當(dāng)前點(diǎn) g[x].get(i)代表所連的線 Queue<Integer> queue = new ArrayDeque<>(); for(int i=1;i<=n;i++){ if(strings[i-1].equals("R")){ queue.add(i); visited[i]=1; } } while(!queue.isEmpty()){ int temp=queue.remove(); for(int i=0;i<g[temp].size();i++){ if(visited[g[temp].get(i)]==0){ visited[g[temp].get(i)]=1; dis[g[temp].get(i)]=dis[temp]+1; queue.add(g[temp].get(i)); } } } for(int i=1;i<=n;i++){ if(visited[i]==0)System.out.print("-1 "); else System.out.print(dis[i]+" "); } } }
對(duì)照上一章的案例:小紅點(diǎn)點(diǎn)點(diǎn)結(jié)合理解。 分別使用的dfs和bfs。
本題思想:先將紅色的所有點(diǎn)都入隊(duì)列,然后bfs。
這是一種逆向思維:不是所謂的從編號(hào)開(kāi)始,并且所有走過(guò)的都不能在走了。
3.小紅玩數(shù)組
難度????
知識(shí)點(diǎn):雙端隊(duì)列
用一個(gè)雙端隊(duì)列來(lái)模擬過(guò)程,用一個(gè)變量來(lái)標(biāo)記雙端隊(duì)列是否翻轉(zhuǎn)過(guò)。
示例1:
輸入
6 4
1 5 4 6 2 8
5
21323
輸出
4 6 2 1 5 8
import java.io.*; import java.util.*; public class Main{ static Deque<Integer> workQueue; public static void main(String[] args)throws IOException{ BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); PrintWriter pw=new PrintWriter(System.out); String[] firstLine=br.readLine().split(" "); int total=Integer.parseInt(firstLine[0]); int size=Integer.parseInt(firstLine[1]); int[] arr=new int[total]; String[] secondLine=br.readLine().split(" "); for(int i=0;i<total;i++){ arr[i]=Integer.parseInt(secondLine[i]); } int L=0; int R=size-1; workQueue=new LinkedList<>(); for(int i=0;i<size;i++){ workQueue.offerLast(arr[i]); } int times=Integer.parseInt(br.readLine()); String tries=br.readLine(); int is=0;//0代表沒(méi)有翻轉(zhuǎn)! for(int i=0;i<times;i++){ if(tries.charAt(i)=='1'){ if(R==arr.length-1) continue; R++; if(is==0){ workQueue.offerLast(arr[R]); int tmp=workQueue.pollFirst(); arr[L]=tmp; }else{ workQueue.offerFirst(arr[R]); int tmp=workQueue.pollLast(); arr[L]=tmp; } L++; }else if(tries.charAt(i)=='2'){ if(L==0) continue; L--; if(is==0){ workQueue.offerFirst(arr[L]); arr[R]=workQueue.pollLast(); }else{ workQueue.offerLast(arr[L]); arr[R]=workQueue.pollFirst(); } R--; }else{ is=1-is; } } for(int i=0;i<L;i++){ pw.print(arr[i]+" "); } if(is==0){ while(!workQueue.isEmpty()) { pw.print(workQueue.pollFirst() + " "); } }else{ while(!workQueue.isEmpty()) { pw.print(workQueue.pollLast() + " "); } } for(int i=R+1;i<arr.length;i++){ pw.print(arr[i]+" "); } pw.flush(); } }
到此這篇關(guān)于Java超詳細(xì)精講數(shù)據(jù)結(jié)構(gòu)之bfs與雙端隊(duì)列的文章就介紹到這了,更多相關(guān)Java bfs與雙端隊(duì)列內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
手把手帶你了解Java-Stream流方法學(xué)習(xí)及總結(jié)
這篇文章主要介紹了通過(guò)實(shí)例了解JavaStream流的方法學(xué)習(xí)和總結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2021-08-08spring boot實(shí)現(xiàn)自動(dòng)輸出word文檔功能的實(shí)例代碼
這篇文章主要介紹了spring boot實(shí)現(xiàn)自動(dòng)輸出word文檔功能的實(shí)例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04舉例講解Java的Spring框架中AOP程序設(shè)計(jì)方式的使用
這篇文章主要介紹了Java的Spring框架中AOP程序設(shè)計(jì)方式的使用講解,文中舉的AOP下拋出異常的例子非常實(shí)用,需要的朋友可以參考下2016-04-04Java class文件格式之屬性_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
在本文中, 主要講解了class文件中的一些屬性。 這些屬性可以出現(xiàn)在class文件中的對(duì)個(gè)地方, 用來(lái)描述一些其他信息2017-06-06解決Spring?Security集成knife4j訪問(wèn)接口文檔出現(xiàn)403的問(wèn)題
這篇文章主要給大家介紹了如何解決Spring?Security集成knife4j訪問(wèn)接口文檔出現(xiàn)403的問(wèn)題,文中有詳細(xì)的解決方案,有需要的朋友可以參考閱讀下2023-07-07Java實(shí)現(xiàn)將枚舉類(lèi)轉(zhuǎn)為json并返回給前端
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)將枚舉類(lèi)轉(zhuǎn)為json并返回給前端的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12