BFS簡介
參考鏈接
廣度優先搜索一層一層地進行遍歷,每層遍歷都以上一層遍歷的結果作爲起點,遍歷一個距離能訪問到的所有節點。需要注意的是,遍歷過的節點不能再次被遍歷。
第一層:
0->{1,2,6,5}
第二層:
6->{4}
5->{3,4}
第三層:
3->{}
4->{}
每一層遍歷的節點都與根節點距離相同。設 di 表示第 i 個節點與根節點的距離,推導出一個結論:對於先遍歷的節點 i 與後遍歷的節點 j,有 di <= dj。利用這個結論,可以求解最短路徑等 最優解 問題:第一次遍歷到目的節點,其所經過的路徑爲最短路徑。應該注意的是,使用 BFS 只能求解無權圖的最短路徑,無權圖是指從一個節點到另一個節點的代價都記爲 1。
在程序實現 BFS 時需要考慮以下問題:
- 隊列:用來存儲每一輪遍歷得到的節點;
- 標記:對於遍歷過的節點,應該將它標記,防止重複遍歷。
DFS簡介
深度優先遍歷顧名思義就是可着一條路去遍歷。以下圖舉例,程序會優先去走一條路,比如會先a->b->e;之後以程序語言來說就是e是葉子節點了,所以結束遞歸,返回b的位置,此時b會取遍歷f,路徑就是a->b->f,之後就是返回a,a->c,a->d。其實上述描述語言爲前序遍歷,當前->左->右。
在程序實現 DFS 時需要考慮以下問題:
棧:用棧來保存當前節點信息,當遍歷新節點返回時能夠繼續遍歷當前節點。可以使用遞歸棧。
標記:和 BFS 一樣同樣需要對已經遍歷過的節點進行標記。
題目
690. 員工的重要性
題目鏈接。本題需要考慮的就是不是直系下屬的重要程度也需要考慮入內。
1.dfs解法:
dfs的思路就是一條路一條路的去走。
以該例子舉例
[[1,5,[2,3]],[2,3,[4]],[3,4,[]],[4,1,[]]]
1
程序的流程是:首先找到員工1,之後找到直系下屬員工2,之後找到非直系下屬4,最後在非直系下屬4的時候結束尋找。也就是傳遞的始終是當前要找的員工id。
路徑爲:1->2->4,1->3。
/*
// Employee info
class Employee {
// It's the unique id of each node;
// unique id of this employee
public int id;
// the importance value of this employee
public int importance;
// the id of direct subordinates
public List<Integer> subordinates;
};
*/
class Solution {
//不是直系下屬的也需要添加進去。
int impo;
public int getImportance(List<Employee> employees, int id) {
//還是dfs最好用我覺得
//id與對應的員工信息
Map<Integer,Employee>map = new HashMap<>();
for(Employee e:employees)
map.put(e.id,e);
dfs(map,id);
return impo;**加粗樣式**
}
public void dfs(Map<Integer,Employee>map,int id)
{
Employee e = map.get(id);
impo += e.importance;
for(Integer i:e.subordinates)
{
dfs(map,i);
}
return ;
}
}
2.bfs算法
bfs算法是一層一層的遍歷。
以該例子舉例
[[1,5,[2,3]],[2,3,[4]],[3,4,[]],[4,1,[]]]
1
程序的流程是,一層一層的去遍歷,首先遍歷的是我們要找的父員工,然後去遍歷父員工的所有直系下屬員工,之後遍歷所有直系下員工的所有直系下屬員工。也就是說每一回遍歷傳遞的是一個列表,列表中存儲當前層的所有id。
路徑爲:
第一層:1;
第二層:2,3;
第三層:4。
/*
// Employee info
class Employee {
// It's the unique id of each node;
// unique id of this employee
public int id;
// the importance value of this employee
public int importance;
// the id of direct subordinates
public List<Integer> subordinates;
};
*/
class Solution {
//不是直系下屬的也需要添加進去。
int impo;
public int getImportance(List<Employee> employees, int id) {
//還是dfs最好用我覺得
//id與對應的員工信息
Map<Integer,Employee>map = new HashMap<>();
for(Employee e:employees)
map.put(e.id,e);
List<Integer>list = new ArrayList<>();
list.add(id);
bfs(map,list);
return impo;
}
public void bfs(Map<Integer,Employee>map,List<Integer>l)
{
List<Integer>list=new ArrayList<>();
for(Integer i:l)
{
Employee e = map.get(i);
impo+=e.importance;
list.addAll(e.subordinates);
}
if(list.size()!=0)
bfs(map,list);
return ;
}
}
547.朋友圈
1.dfs解法
這道題我的思路就是深度優先遍歷。其實是兩層遍歷,第一層是遍歷每個學生,第二層是遍歷每個學生和他有好友關係的學生。第一層的含義其實是循環一次就代表一個朋友圈,因爲我們要做的是有記憶功能的dfs。我們使用一個boolean型數組來存儲,flag【i】代表i是否已經在一個朋友圈裏了。當該學生不在朋友圈中,此時進入dfs遞歸。在dfs中我們去把該學生的好友關係挨個尋找一遍,以此類推,直到學生的好友關係的那些好友都已經在某個朋友圈當中了。
首先要理解第一個循環走一次就代表一個朋友圈,因爲我會在這一次裏把所有與該A學生有關的學生全部找到(不是直接有關係,有間接關係的也會找到),這些都在一個朋友圈內。下一次去找的學生是與他們一點關係都沒有的學生,然後和這個學生有關係的學生們會組成另一個朋友圈。所以第一個循環走幾次就代表有幾個朋友圈。
其次,dfs中會遇到兩個誤導。
1.是否可以從(M【i】數組)i位置往後走,因爲i位置以前的學生我都看過了。
答案是否定的。因爲比如有四個學生。有可能第一個學生和第二個學生沒關係,和第三個學生有關係,那麼我們去找第三個學生的時候,就直接去看第三個和第四個學生是否有關係,那萬一第三個和第二個學生還有關係呢?
更改做法:所以我們每次都必須從0開始往後找。
2.是否可以在dfs後再將flag【j】賦值爲true呢?
答案是否定的。設想第二個學生和第三個學生有關係,那麼我們會在M【2】數組的第三個位置進入dfs循環,之後又會在M【3】數組的第二個位置進入上一個dfs循環,這樣就會一直循環。
**更改做法:**我們在進入dfs的時候將flag【i】設爲true,因爲我們是從i學生走過來的,所以我們要將i學生放入我們的朋友圈。
class Solution {
int circles = 0;
//有存儲記憶
boolean[]flag;
public int findCircleNum(int[][] M) {
//代表i是否已經在一個朋友圈了
flag = new boolean[M.length];
for(int i=0;i<M.length;i++){
if(flag[i])
continue;
//循環幾次就是有幾個朋友圈(因爲能循環,就代表這個學生沒有被加入到朋友圈之中)
circles++;
dfs(M,flag,i);
}
return circles;
}
public void dfs(int[][]M,boolean[]flag,int i) {
//列i之前的不用看了,肯定都看過了。
//因爲i行之前的行都看過了。
//要看i學生,是否能和後面的學生繼續互爲朋友關係。
//上面的是錯的,因爲比如有四個學生
//有可能第一個學生和第二個學生沒關係,和第三個學生有關係,
//那麼我們去找第三個學生的時候,就直接去看第三個和第四個學生是否有關係,那萬一第三個和第二個學生還有關係呢?
//我們把第i個同學加進我們的朋友圈。
flag[i]=true;
for(int j=0;j<M[0].length;j++)
{
if(flag[j]||i==j)
continue;
if(M[i][j]==1)
{
dfs(M,flag,j);
//這會導致我們一直在dfs循環中,比如第二個和第三個有關係,那麼第三個也和第二個有關係。
//就會不斷的循環dfs(M,flag,1),和dfs(M,flag,2),去dfs一次就應該把當前循環的那個
//學生加入到朋友圈之中,但不能再dfs之後,應該在dfs初始就加入,防止陷入死循環。
//flag[j]=false;
}
}
}
}