Leetcode一起攻克搜索(BFS,DFS,回溯)

BFS簡介

參考鏈接
在這裏插入圖片描述
廣度優先搜索一層一層地進行遍歷,每層遍歷都以上一層遍歷的結果作爲起點,遍歷一個距離能訪問到的所有節點。需要注意的是,遍歷過的節點不能再次被遍歷。
第一層:
0->{1,2,6,5}
第二層:
6->{4}
5->{3,4}
第三層:
3->{}
4->{}
每一層遍歷的節點都與根節點距離相同。設 di 表示第 i 個節點與根節點的距離,推導出一個結論:對於先遍歷的節點 i 與後遍歷的節點 j,有 di <= dj。利用這個結論,可以求解最短路徑等 最優解 問題:第一次遍歷到目的節點,其所經過的路徑爲最短路徑。應該注意的是,使用 BFS 只能求解無權圖的最短路徑,無權圖是指從一個節點到另一個節點的代價都記爲 1。
在程序實現 BFS 時需要考慮以下問題:

  1. 隊列:用來存儲每一輪遍歷得到的節點;
  2. 標記:對於遍歷過的節點,應該將它標記,防止重複遍歷。

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;
            }
        }
        
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章