簡單來說樹就是無向、連通、無環的圖,所以前面的DFS、BFS概念可以延用,只不過要注意圖可能會出現環(cycle)所以需要額外變數來記錄是否走過,以免出現無窮迴圈。
特性 | DFS | BFS |
---|---|---|
核心資料結構 | Stack | Queue |
最短路徑 | 可能繞遠 | 無權重下保證最短 |
適合題目 | 列出所有組合、排列、完整路徑 | 最短路徑 、找所有最短解 |
照慣例刷個題練習一下
題目:給一個有向無環圖(DAG)列出所有路徑
輸入:graph = [[1,2],[3],[3],[]]
輸出:[0,1,3],[0,2,3]]
限制:
n == graph.length
2 <= n <= 15
0 <= graph[i][j] < n
graph[i][j] != i
(i.e., there will be no self-loops).
All the elements of graph[i]
are unique.
The input graph is guaranteed to be a DAG.
DFS解法:因為有向無環所以沒有重複走的問題,我們使用深度優先每題路徑走完再換另一條。
// [[4,3,1],[3,2,4],[3],[4],[]] n=5 from 0 to 4
// 1st 0->4
// 2nd 0->3->4
// 3rd-1 0->1->3-4
// 3rd-2 0->1->2->3->4
// 3rd-3 0->1->4
impl Solution {
pub fn all_paths_source_target(graph: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
let mut ans = Vec::new();
let mut path = vec![0];
let n = graph.len() as i32;
Self::back_tracking(&graph,0,n-1,&mut path,&mut ans);
ans
}
pub fn back_tracking(graph:&Vec<Vec<i32>>,node:i32, target:i32, path:&mut Vec<i32>,ans:&mut Vec<Vec<i32>>) {
if node==target {
ans.push(path.clone());
return
}
for &i in &graph[node as usize] {
path.push(i);
Self::back_tracking(graph,i,target,path,ans);
path.pop();
}
}
}
BFS解法:從起點開始,列出目前能走的路再從目前能走的路向外延伸
//BFS
//init graph=[[1,2],[3],[3],[]] path:[[0]] ans:[]
//loop path take[0] BFS graph[0] path=[[0,1],[0,2]] ans:[]
//loop path take[0,1] BFS graph[1] push element==n-1 [0,1,3] add to ans path=[[0,2]] ans:[[0,1,3]]
//loop path take[0,2] BFS graph[2] push element==n-1 [0,2,3] path=[] ans:[[0,1,3],[0,2,3]]
use std::collections::VecDeque;
impl Solution {
pub fn all_paths_source_target(graph: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
let finish = graph.len() as i32 -1;
let mut deque=VecDeque::new();
let mut ans:Vec<Vec<i32>> = Vec::new();
deque.push_back(vec![0]);
while deque.len()>0 {
let Some(front)=deque.pop_front() else { break};
let last = *front.last().expect("must have 0");
for &element in &graph[last as usize] {
let mut new_path=front.clone();
new_path.push(element);
// let new_path=vec![1];
if(element==finish){
ans.push(new_path);
} else {
deque.push_back(new_path);
}
}
}
ans
}
}
時間複雜度:都是** **O(2^n )
空間複雜度:
DFS: O(n),一條路徑。
BFS: O(2^n),全部路徑。
我們用之前提到的leetcode1091為例,如果改成DFS,會變成比較每一條走到終點的路徑長度全部比完才會回傳答案,相比BFS第一個走到的就是最短路徑時間,DFS效率沒那麼好。
// use DFS to fin shortest path
//start from 0 to 8 directions[(1,0),(1,1),(0,1),(-1,1),(-1,0),(-1,-1),(0,-1),(1,-1)]
//if direction (1,0) is 0 go ahead until (n-1,n-1)
// if position is 1 or path bigger than shortest path return
// everytime achive update the shortest path
impl Solution {
pub fn shortest_path_binary_matrix(mut grid: Vec<Vec<i32>>) -> i32 {
// check start and finish !=1
let n = grid.len();
if(grid[0][0]==1 || grid[n-1][n-1]==1){
return -1
}
//start DFS
let mut shortest = i32::MAX;
const directions:[(i32, i32); 8]= [
(1,0),
(1,1),
(0,1),
(-1,1),
(-1,0),
(-1,-1),
(0,-1),
(1,-1)
];
Self::DFS(&mut grid,0,0,1,&mut shortest,&directions);
if(shortest==i32::MAX){
return -1
}
return shortest
}
pub fn DFS(grid:&mut Vec<Vec<i32>>, x:i32, y:i32, mut path:i32, shortest: &mut i32,directions:&[(i32, i32); 8]){
if(path>=*shortest){
return
}
if(x as usize==grid[0].len()-1 && y as usize==grid.len()-1){
*shortest = *shortest.min(&mut path);
return
}
grid[x as usize][y as usize] = 1;
for (dx,dy) in directions {
// check boundary
if(x+dx<0 || x+dx>=grid[0].len() as i32 || y+dy<0 || y+dy>=grid.len() as i32){
continue
}
//check position is 1
if(grid[(x+dx) as usize][(y+dy) as usize]==1){
continue
}
Self::DFS(grid,x+dx,y+dy,path+1,shortest,directions)
}
grid[x as usize][y as usize] = 0;
}
}
跑測資再matrix很大的情況下會超時