LeetCode指北-BFS

BFS就是廣度優(yōu)先算法,BFS相對(duì)DFS來(lái)說(shuō)不太直觀。BFS中,我們會(huì)搜索r的“孫子節(jié)點(diǎn)”之前先訪問(wèn)結(jié)點(diǎn)r的所有相鄰結(jié)點(diǎn)。一般用隊(duì)列+迭代方案實(shí)現(xiàn)。

為了復(fù)習(xí)下BFS的思想,看下BFS最基本的場(chǎng)景=。=
無(wú)向圖的BFS來(lái)舉栗子??
BFS是“地毯式”層層遞進(jìn)的搜索策略,先查找離起始頂點(diǎn)最近的,依次往外搜索。下圖表示了搜索的過(guò)程,盜一張大佬的圖。

其實(shí)思想不復(fù)雜,實(shí)現(xiàn)起來(lái)還是要琢磨一下的。

前面提到BFS要用隊(duì)列+迭代方案實(shí)現(xiàn),那么我們需要三個(gè)輔助變量visited,queue,prev

visited:記錄以及訪問(wèn)過(guò)的頂點(diǎn),避免頂點(diǎn)被重復(fù)訪問(wèn)
queue:關(guān)鍵的是這里,實(shí)現(xiàn)的是記錄功能,因?yàn)槭菍訉舆f進(jìn),需要暫存訪問(wèn)過(guò)的頂點(diǎn),方便進(jìn)行下一層搜索
prev:記錄搜索過(guò)的路徑,不過(guò)是反向存儲(chǔ)的,prev[w]存儲(chǔ)的是從哪個(gè)前驅(qū)頂點(diǎn)遍歷過(guò)來(lái)的。關(guān)注下print函數(shù)的實(shí)現(xiàn)。

talk is cheap,show you the code:

public class BFS {
    public int v;//頂點(diǎn)個(gè)數(shù)
    public LinkedList<Integer> adj[];// 鄰接表

    public BFS(int v) {
        this.v = v;
        adj = new LinkedList[v];
        for (int i = 0; i < v; ++i) {
            adj[i] = new LinkedList<>();
        }
    }

    // 添加連接關(guān)系
    public void addEdge(int s, int t) {
        adj[s].add(t);
        adj[t].add(s);
    }

    // bfs搜索,s到t的路徑
    public void bfs(int s, int t) {
        // s,t是同一節(jié)點(diǎn),返回
        if (s == t) {
            return;
        }

        //三大輔助變量---visited
        boolean[] visited = new boolean[v];

        // 三大輔助變量---queue
        Queue<Integer> queue = new LinkedList<>();

        // 加入s
        queue.add(s);

        // 三大輔助變量-- prev
        int[] prev = new int[v];

        // 初始化為-1
        for (int i = 0; i < v; ++i) {
            prev[i] = -1;
        }

        while (queue.size() != 0) {
            // 堆頂出隊(duì)
            int w = queue.poll();

            // 遍歷w的臨接節(jié)點(diǎn)
            for (int i = 0; i < adj[w].size(); ++i) {
                //和w相鄰的一個(gè)節(jié)點(diǎn)
                int q = adj[w].get(i);
                // 如果q沒(méi)有訪問(wèn)過(guò)
                if (!visited[q]) {
                    // 記錄prev,q的上前驅(qū)節(jié)點(diǎn)是w
                    prev[q] = w;
                    // 到終點(diǎn)了打印路徑
                    if (q == t) {
                        print(prev, s, t);
                        return;
                    }
                    // q訪問(wèn)過(guò)了,標(biāo)記
                    visited[q] = true;
                    // 將q加入隊(duì)列
                    queue.add(q);
                }
            }
        }

    }

    // 遞歸打印從終點(diǎn)t到起點(diǎn)的路徑
    private void print(int[] prev, int s, int t) {
        if (prev[t] != -1 && t != s) {
            print(prev, s, prev[t]);
        }
        System.out.print(t + " ");
    }
}

其實(shí)呢,要記住模板~編碼5分鐘,調(diào)試兩小時(shí),再放一個(gè)方便記憶的模板,也方便手寫(xiě)??

void search(Node root) {
        Queue queue = new Queue();
        root.visited = true;
        visit(root);
        queue.add(root); // 加至隊(duì)列尾部
      
       while(!queue.isEmpty())  {
           Node r = queue.poll(); //從隊(duì)列頭部移除
           foreach(Node n in r.adjacent) {
                if(n.visited == false) {
                    visit(n);
                    n.visited = true;
                    queue.add(n);
                }
          }
      }
}

下面實(shí)操一下,看看LeetCode真題??


最短路徑的題,“地毯式搜索”,廣度優(yōu)先返回的就是最短路線

  • visited數(shù)組,記錄訪問(wèn)過(guò)的數(shù)字,是從0到n
  • queue 這里存放的挑選后剩余的總和n,以及已選數(shù)字的個(gè)數(shù)
  • 滿足條件時(shí),返回
  • 用java寫(xiě)感覺(jué)有點(diǎn)啰嗦,=。=
    public int numSquares(int n) {
        if (n == 0) {
            return 0;
        }

        // 暫存隊(duì)列,每一種元素有剩余總數(shù)和已選元素個(gè)數(shù)組成
        Deque<ArrayList<Integer>> queue = new LinkedList<ArrayList<Integer>>();
        ArrayList list = new ArrayList(2);
        list.add(n);
        list.add(0);
        queue.add(list);

        boolean[] visited = new boolean[n + 1];
        visited[n] = true;

        while (!queue.isEmpty()) {
            ArrayList<Integer> temp = queue.removeFirst();
            int remain = temp.get(0);
            int step = temp.get(1);
            if(remain == 0) {
                return step;
            }
            for (int i = 1; remain - i * i >= 0; i++) {
                int a = remain - i * i;
                if (!visited[a]) {
                    if (a == 0) {
                        return step+1;
                    }
                    ArrayList tempList = new ArrayList(2);
                    tempList.add(a);
                    tempList.add(step + 1);
                    queue.addLast(tempList);
                    visited[a] = true;
                }

            }
        }
        return 0;
    }

再看一題:


根據(jù)題目的意思,就是要廣搜來(lái)遍歷對(duì)應(yīng)id的員工所有impance的值

  • 使用了java 8的stream來(lái)篩選出對(duì)應(yīng)id的employee的信息
       public int getImportance(List<Employee> employees, int id) {
        int result = 0;
        Employee rootEmployee = new Employee();
        Queue<Employee> queue = new LinkedList<>();
        rootEmployee = getEmployeeById(employees, id);
        queue.offer(rootEmployee);
        while (!queue.isEmpty()) {
            Employee employee = queue.poll();
            result += employee.importance;
            for (Integer subordinate : employee.subordinates) {
                queue.offer(getEmployeeById(employees, subordinate));
            }
        }
        return result;

    }

    private Employee getEmployeeById(List<Employee> employees, int id) {
        return employees.stream().filter(employee -> employee.id == id).collect(Collectors.toList()).get(0);
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容