九章算法筆記 4.寬度優先搜索 Breadth First Search

算法與題型 cs3k.com

  1. DFS: 用於搜索, 題目中有ALL字樣
  2. 二分法: 用於時間複雜度小於O(n)的狀況
  3. 分治法: 二叉樹問題, 子問題和父問題有關係
  4. BFS:- 二叉樹上的寬搜- 圖上的寬搜: 拓撲排序- 棋盤上的寬搜

 

何時應該用BFS?

  1. 圖的遍歷 Traversal in Graph:
    – 層級遍歷 Level Order Traversal: 有先碰到後碰到的問題, 分距離遠近enter image description here
    – 由點及面 Connected Component: 聯通問題, 好比Smallest Rectangle Enclosing Black Pixels這道題就能夠用灌水法作: 二分法O(Row * logCol + Col * LogRow), 灌水法O(R * C)- 拓撲排序 Topological Sorting: 有向圖********pic4.2
  2. 最短路徑 Shortest Path in Simple Graph
    – 僅限簡單圖求最短路徑(圖中每條邊長度都是1,且沒有方向)
    – ps: 若是問最長的路徑呢? 用DP或者dfs全部路徑找一遍

Binary Tree Level Order Traversal

 cs3k.com

VECTOR用ARRAY來實現:

開一個n的區間, 指針指向開頭; 不夠的時候, 再開一個2n的區間, 把指針挪過來.node

用循環數組實現先進先出的隊列:

FIFO隊列包含兩個基本操做:插入(put)一個新的項,刪除(get)一個最先插入的項。算法

循環數組實現方式:express

對於每個隊列數據結構,咱們保留一個數組S,其最多存放的Nmax個元素,定義兩個位置front和rear分別指向隊列的頭尾兩端,此外咱們用N來記錄隊列中實際存在的元素 的個數。編程

對於一個元素item入隊,咱們讓N和rear增1,而後置s[rear]=item;
  對於一個元素item入隊,咱們讓N減1,讓front增1.

注意:這種實現有一個潛在的問題。假設數組的Nmax=10,通過10次入隊後,隊列彷佛已經滿了,由於此時rear=9.然而,隊列中可能只存在小於9個的元素,由於 以前入隊的操做可能伴隨着出隊的操做。例如咱們將1,2,3,4,5,6,7,8,9,10一次入隊,而後又進行2次出隊操做,此時front指向數組元素S[2]處的位置,此時隊列中只 有8個元素,這時rear仍然指向數組的最後一個元素S[Nmax-1]的位置,此時咱們有沒有辦法繼續進行入隊操做呢?數組

簡單的解決方法是:只要front和rear到達數組的末尾,它就又繞回到開頭,這種實現方式就是循環數組的實現。這樣隊列的操做就侷限在所定義的數組這塊存儲空間內。網絡

STACK SYNTAX:

push, pop, top數據結構

snipaste_20170320_095618

QUEUE SYNTAX

push, pop,front,backapp

這道題的實現:

Given a binary tree, return the level order traversal of its nodes’ values. (ie, from left to right, level by level).ide

enter image description here

  1. 模板通常是一個while大循環以後一個for小循環.
  2. 31行爲何要先取出queue的size呢?由於for循環裏面的檢查條件, 每次都執行一遍, 不是開始多大就多大.
  3. 通常不能用stack, 由於同一層的節點會反過來.
    stack: DFS
    queue: BFS
  4. 分層遍歷比不分層遍歷多一個for循環
  5. 時空複雜度都是O(n), ps:通常二叉樹問題的時空複雜度都是O(n).
    通常時間複雜度都是看最裏面的循環體進出了多少次,這道題的while和for循環的最壞狀況都是n次,可是時間複雜度不是n*n,是由於最壞的狀況不能同時發生.

Binary Tree Serialization

 cs3k.com

什麼是序列化?

將「內存」中結構化的數據變成「字符串」的過程wordpress

序列化:object to string

反序列化:string to object

何時須要序列化?

  1. 將內存中的數據持久化存儲時內存中重要的數據不能只是呆在內存裏,這樣斷電就沒有了,所需須要用一種方式寫入硬盤,在須要的時候,可否再從硬盤中讀出來在內存中從新建立
  2. 網絡傳輸時機器與機器之間交換數據的時候,不可能互相去讀對方的內存。只能講數據變成字符流數據(字符串)後經常使用的一些序列化手段:XMLJson是個hash map,key是string, value是list/int或另外一個hash map
    Thrift (by Facebook)
    ProtoBuf (by Google)

一些序列化的例子:

  1. 好比一個數組,裏面都是整數,咱們能夠簡單的序列化爲」[1,2,3]」
  2. 一個整數鏈表,咱們能夠序列化爲,」1->2->3」
  3. 一個哈希表(HashMap),咱們能夠序列化爲,」{\」key\」: \」value\」}」

序列化算法設計時須要考慮的因素:

  1. 壓縮率。對於網絡傳輸和磁盤存儲而言,固然但願更節省如 Thrift, ProtoBuf 都是爲了更快的傳輸數據和節省存儲空間而設計的
  2. 可讀性。咱們但願開發人員,可以經過序列化後的數據直接看懂原始數據是什麼如 Json,LintCode 的輸入數據

 

二叉樹如何序列化?

 
你可使用任何方式進行序列化, bfs,dfs, 前中後序都行,只要serialization以後unserialization回來的同樣就能夠.
 

Binary Tree Serialization

 cs3k.com

Design an algorithm and write code to serialize and deserialize a binary tree. Writing the tree to a file is called ‘serialization’ and reading back from the file to reconstruct the exact same binary tree is ‘deserialization’.
 

Example

An example of testdata: Binary tree {3,9,20,#,#,15,7}, denote the following structure:

3
 / \
9  20
  /  \
 15   7
class Solution { /** * This method will be invoked first, you should design your own algorithm * to serialize a binary tree which denote by a root node to a string which * can be easily deserialized by your own "deserialize" method later. */ public String serialize(TreeNode root) { if (root == null) { return "{}"; } ArrayList<TreeNode> queue = new ArrayList<TreeNode>(); queue.add(root); for (int i = 0; i < queue.size(); i++) { TreeNode node = queue.get(i); if (node == null) { continue; } queue.add(node.left); queue.add(node.right); } while (queue.get(queue.size() - 1) == null) { queue.remove(queue.size() - 1); } StringBuilder sb = new StringBuilder(); sb.append("{"); sb.append(queue.get(0).val); for (int i = 1; i < queue.size(); i++) { if (queue.get(i) == null) { sb.append(",#"); } else { sb.append(","); sb.append(queue.get(i).val); } } sb.append("}"); return sb.toString(); } /** * This method will be invoked second, the argument data is what exactly * you serialized at method "serialize", that means the data is not given by * system, it's given by your own serialize method. So the format of data is * designed by yourself, and deserialize it here as you serialize it in * "serialize" method. */ public TreeNode deserialize(String data) { if (data.equals("{}")) { return null; } String[] vals = data.substring(1, data.length() - 1).split(","); ArrayList<TreeNode> queue = new ArrayList<TreeNode>(); TreeNode root = new TreeNode(Integer.parseInt(vals[0])); queue.add(root); int index = 0; boolean isLeftChild = true; for (int i = 1; i < vals.length; i++) { if (!vals[i].equals("#")) { TreeNode node = new TreeNode(Integer.parseInt(vals[i])); if (isLeftChild) { queue.get(index).left = node; } else { queue.get(index).right = node; } queue.add(node); } if (!isLeftChild) { index++; } isLeftChild = !isLeftChild; } return root; } } 
 

圖上的寬度優先搜索

 cs3k.com

  1. 和樹上的有什麼區別?
    – 圖上可能有環
  2. 因此圖上的須要個hash map或者一個hash set記錄走沒走過這個節點

Graph Valid Tree

Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), write a function to check whether these edges make up a valid tree.

 

一個graph是否是tree的決定條件有兩個:

  1. 點比邊個數多一個
  2. 全部點都要連通: 對於連通性問題, bfs是最好的方法之一.
public class Solution { /** * @param n an integer * @param edges a list of undirected edges * @return true if it's a valid tree, or false */ public boolean validTree(int n, int[][] edges) { if (n == 0) { return false; } if (edges.length != n - 1) { return false; } Map<Integer, Set<Integer>> graph = initializeGraph(n, edges); // bfs Queue<Integer> queue = new LinkedList<>(); Set<Integer> hash = new HashSet<>(); queue.offer(0); hash.add(0); while (!queue.isEmpty()) { int node = queue.poll(); for (Integer neighbor : graph.get(node)) { if (hash.contains(neighbor)) { continue; } hash.add(neighbor); queue.offer(neighbor); } } return (hash.size() == n); } private Map<Integer, Set<Integer>> initializeGraph(int n, int[][] edges) { Map<Integer, Set<Integer>> graph = new HashMap<>(); for (int i = 0; i < n; i++) { graph.put(i, new HashSet<Integer>()); } for (int i = 0; i < edges.length; i++) { int u = edges[i][0]; int v = edges[i][1]; graph.get(u).add(v); graph.get(v).add(u); } return graph; } } 
 
 
1. 咱們須要一個鄰接表adjacent list, 便是一個Map<Integer, Set<Integer>>
2. 實現方式爲queque + hash
3. 兩個循環: while + for
 

Clone Graph

 cs3k.com

Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors.

How we serialize an undirected graph:

Nodes are labeled uniquely.

We use # as a separator for each node, and , as a separator for node label and each neighbor of the node.

As an example, consider the serialized graph {0,1,2#1,2#2,2}.

The graph has a total of three nodes, and therefore contains three parts as separated by #.

  1. First node is labeled as 0. Connect node 0 to both nodes 1 and 2.
  2. Second node is labeled as 1. Connect node 1 to node 2.
  3. Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a self-cycle.

Visually, the graph looks like the following:

1
  / \
 /   \
0 --- 2
     / \
     \_/
 
public class Solution { /** * @param node: A undirected graph node * @return: A undirected graph node */ public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) { if (node == null) { return node; } // use bfs algorithm to traverse the graph and get all nodes. ArrayList<UndirectedGraphNode> nodes = getNodes(node); // copy nodes, store the old->new mapping information in a hash map HashMap<UndirectedGraphNode, UndirectedGraphNode> mapping = new HashMap<>(); for (UndirectedGraphNode n : nodes) { mapping.put(n, new UndirectedGraphNode(n.label)); } // copy neighbors(edges) for (UndirectedGraphNode n : nodes) { UndirectedGraphNode newNode = mapping.get(n); for (UndirectedGraphNode neighbor : n.neighbors) { UndirectedGraphNode newNeighbor = mapping.get(neighbor); newNode.neighbors.add(newNeighbor); } } return mapping.get(node); } private ArrayList<UndirectedGraphNode> getNodes(UndirectedGraphNode node) { Queue<UndirectedGraphNode> queue = new LinkedList<UndirectedGraphNode>(); HashSet<UndirectedGraphNode> set = new HashSet<>(); queue.offer(node); set.add(node); while (!queue.isEmpty()) { UndirectedGraphNode head = queue.poll(); for (UndirectedGraphNode neighbor : head.neighbors) { if(!set.contains(neighbor)){ set.add(neighbor); queue.offer(neighbor); } } } return new ArrayList<UndirectedGraphNode>(set); } } 

 

三步走:

  1. 經過一個點找到全部的點
  2. 克隆點, 即點變新的點
  3. 克隆邊, 即邊變新的邊

勸分不勸合, 編程能分開的儘可能分開寫。

能用BFS, 必定不用DFS, 由於dfs有recursion, 容易stack over flow

Search Graph Nodes

 cs3k.com

由點及面

不用作分層遍歷

Given a undirected graph, a node and a target, return the nearest node to given node which value of it is target, return NULL if you can’t find.

There is a mapping store the nodes’ values in the given parameters.

It’s guaranteed there is only one available solution

 

Example

 

2------3  5
 \     |  | 
  \    |  |
   \   |  |
    \  |  |
      1 --4
Give a node 1, target is 50

there a hash named values which is [3,4,10,50,50], represent:
Value of node 1 is 3
Value of node 2 is 4
Value of node 3 is 10
Value of node 4 is 50
Value of node 5 is 50

Return node 4
public class Solution { /** * @param graph a list of Undirected graph node * @param values a hash mapping, <UndirectedGraphNode, (int)value> * @param node an Undirected graph node * @param target an integer * @return the a node */ public UndirectedGraphNode searchNode(ArrayList<UndirectedGraphNode> graph, Map<UndirectedGraphNode, Integer> values, UndirectedGraphNode node, int target) { // Write your code here Queue<UndirectedGraphNode> queue = new LinkedList<UndirectedGraphNode>(); Set<UndirectedGraphNode> hash = new HashSet<UndirectedGraphNode>(); queue.offer(node); hash.add(node); while (!queue.isEmpty()) { UndirectedGraphNode head = queue.poll(); if (values.get(head) == target) { return head; } for (UndirectedGraphNode nei : head.neighbors) { if (!hash.contains(nei)){ queue.offer(nei); hash.add(nei); } } } return null; } }

 

Topological Sorting

 cs3k.com

-能夠用DFS?

能夠,可是不推薦

-應用場景

選課

編譯:編譯的時候用link的東西,因此要看看有沒有循環依賴。

-關心可否進行拓撲排序, 便是關心有沒有環

-入度: 指向一個節點的邊的個數

 

Given an directed graph, a topological order of the graph nodes is defined as follow:

  • For each directed edge A -> B in graph, A must before B in the order list.
  • The first node in the order can be any node in the graph with no nodes direct to it.

Find any topological order for the given graph.

 

 
Example

 

For graph as follow:

picture

The topological order can be:

[0, 1, 2, 3, 4, 5]
[0, 2, 3, 1, 5, 4]
...

 

Screenshot from 2017-10-02 23-21-19

Course Schedule

There are a total of n courses you have to take, labeled from 0 to n - 1.

Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]

Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?

 

Example

 

Given n = 2, prerequisites = [[1,0]]
Return true

Given n = 2, prerequisites = [[1,0],[0,1]]
Return false

 

Screenshot from 2017-10-02 23-25-31.png

相關文章
相關標籤/搜索