zoukankan      html  css  js  c++  java
  • 五、宽度优先搜索(BFS)

    用处:

    树是图的特殊状态

    图的遍历 Traversal in Graph

      • 层级遍历 Level Order Traversal

       • 由点及面 Connected Component

      • 拓扑排序 Topological Sorting

    最短路径 Shortest Path in Simple Graph

       • 仅限简单图求最短路径

       • 即,图中每条边长度都是1,且没有方向

    用queue去存每一层,queue的实现LinkedList<>或ArrayDeque,不能是ArrayList会浪费时间

    queue.offer() 加入队列,queue.poll()删除并取出first元素,不用add和pop异常处理不同。

    DFS:模板

    一、二叉树上的bfs

    1、Binary Tree Level Order Traversal : 树的层级遍历  http://www.lintcode.com/problem/binary-tree-level-order-traversal/

    not ac :queue.isEmpty() 写错,results 写错,level.add加节点值

    Deque LinkedList<>

    public class Solution {
        /**
         * @param root: The root of binary tree.
         * @return: Level order a list of lists of integer
         */
        public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) {
            // write your code here
            ArrayList<ArrayList<Integer>> results = new ArrayList<>();
            if (root == null) {
                return results;
            }
            Deque<TreeNode> queue = new LinkedList<TreeNode>();
            queue.offer(root);
            while(!queue.isEmpty()) {
                ArrayList<Integer> level = new ArrayList<>();
                int size = queue.size();
                for (int i = 0; i < size; i++) {
                    TreeNode head = queue.poll();
                    level.add(head.val);
                    if (head.left != null) {
                        queue.offer(head.left);
                    }
                    if (head.right != null) {
                        queue.offer(head.right);
                    }
                }
                results.add(level);
            }
            return results;
        }
    }
    View Code

    2、序列化: 将“内存”中结构化的数据变成“字符串”的过程 序列化:object to string 反序列化:string to object

    什么时候需要序列话:

    1. 将内存中的数据持久化存储时 内存中重要的数据不能只是呆在内存里,这样断电就没有了,所需需要用一种方式写入硬盘,在需要的 时候,能否再从硬盘中读出来在内存中重新创建

    2. 网络传输时 机器与机器之间交换数据的时候,不可能互相去读对方的内存。只能讲数据变成字符流数据(字符串)后 通过网络传输过去。接受的一方再将字符串解析后到内存中。

    常用的一些序列化手段: • XML • Json • Thrift (by Facebook) • ProtoBuf (by Google)

    3、序列化算法设计考虑:

     • 压缩率。对于网络传输和磁盘存储而言,当然希望更节省。 • 如 Thrift, ProtoBuf 都是为了更快的传输数据和节省存储空间而设计的。

    • 可读性。我们希望开发人员,能够通过序列化后的数据直接看懂原始数据是什么。 • 如 Json,LintCode 的输入数据

    4、二叉树序列话

    任何法进行序列化,只要保证能够解析回来即可。

    LintCode 采用的是 BFS 的方式对二叉树数据进行序列化,这样的好处是,你可以更为容易的自己画出 整棵二叉树。

    1) http://www.lintcode.com/en/problem/binary-tree-serialization/

    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) {
            // write your code here
            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) {
            // write your code here
            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 isLeft = true;
            for (int i = 1; i < vals.length; i++) {
                if (!vals[i].equals("#")) {
                    TreeNode node = new TreeNode(Integer.parseInt(vals[i]));
                    if (isLeft) {
                        queue.get(index).left = node;
                    } else {
                        queue.get(index).right = node;
                    }
                    queue.add(node);
                }
                if (!isLeft) {
                    index++;
                }
                isLeft = !isLeft;
            }
            return root;
        }    
    }
    View Code

    5、图的序列化

    如何表示图的邻接表:Map<Integer, Set<Integer>> 点 -- 和该点相邻的节点

    如何判断图是树:1、边比点数少1

            2、判断连通性:是否有一个点没和任何点相连,从一点能访问任何一点

    1)Graph Valid Tree   http://www.lintcode.com/problem/graph-valid-tree/

    思路: 判断条件1

       初始化图:表示邻接表

       判断条件2

        public boolean validTree(int n, int[][] edges) {
            // Write your code here
            //判断条件1:边和点数
            if (n == 0) {
                return false;
            }
            if (edges.length != n - 1) {
                return false;
            }
            //初始化
            Map<Integer, Set<Integer>> graph = initializeGraph(n, edges);
            //判断条件2:连通性
            Deque<Integer> queue = new LinkedList<Integer>();
            HashSet<Integer> hash = new HashSet<>();
            queue.offer(0);
            hash.add(0);
            while (!queue.isEmpty()) {
                int node = queue.poll();
                for (Integer neighour : graph.get(node)) {
                    if (hash.contains(neighour)) {
                        continue;
                    }
                    hash.add(neighour);
                    queue.offer(neighour);
                }
            }
            return hash.size() == n;
        }
        public 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;
        }
    }
    View Code

    2)Clone Graph (F) http://www.lintcode.com/problem/clone-graph/  克隆无相图

    ***3步

    1、得到所有的点 ArrayList<节点> nodes =   

        bfs: queue 和 hashmap

    2、克隆点,old -> new映射关系

    3、克隆边

    记得对空检查

    public class Solution {
        /**
         * @param node: A undirected graph node
         * @return: A undirected graph node
         */
        public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
            // write your code here
            // 1.use bfs algorithm to traverse the graph and get all nodes.
            if (node == null) {
                return node;
            }
            ArrayList<UndirectedGraphNode> nodes = getNodes(node);
            //2. copy nodes, store the old->new mapping information in a hash map
            HashMap<UndirectedGraphNode, UndirectedGraphNode> map = new HashMap<>();
            for (UndirectedGraphNode n : nodes) {
                map.put(n, new UndirectedGraphNode (n.label));
            }
            //3.copy neighbors(edges)
            for (UndirectedGraphNode n : nodes) {
                UndirectedGraphNode  newNode = map.get(n);
                for (UndirectedGraphNode neighbor : n.neighbors) {
                    UndirectedGraphNode newNeighbor = map.get(neighbor);
                    newNode.neighbors.add(newNeighbor);
                }
            }
            return map.get(node);
        }
        private ArrayList<UndirectedGraphNode> getNodes(UndirectedGraphNode node) {
            Deque<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);
        }
    }
    View Code

    3)Topological Sorting http://www.lintcode.com/problem/topological-sorting/ 拓扑排序

    **1找根节点:没有指向它的节点---用hashmap<邻居节点,该邻接节点被指向的个数>,根节点没存在这个map里

     2.将该节点加入结果,依次从此节点开始访问

      

    public class Solution {
        /**
         * @param graph: A list of Directed graph node
         * @return: Any topological order for the given graph.
         */    
        public ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) {
            // write your code here
            ArrayList<DirectedGraphNode> results = new ArrayList<>();
            HashMap<DirectedGraphNode,Integer> map = new HashMap<>();
            //用map记录 所有  被指向的节点 和 被指向的次数
            for (DirectedGraphNode node : graph) {
                for (DirectedGraphNode neighbor: node.neighbors) {
                    if (map.containsKey(neighbor)) {
                        map.put(neighbor, map.get(neighbor) + 1);
                    } else {
                        map.put (neighbor, 1);
                    }
                }
            }
            Deque<DirectedGraphNode> queue = new LinkedList<DirectedGraphNode>();
            //找到未被指向的根节点,用queue记录根节点,并把该节点加入results
            for (DirectedGraphNode node : graph) {
                if (!map.containsKey(node)) {
                    queue.offer(node);
                    results.add(node);
                }
            }
            //从根节点遍历
            while (!queue.isEmpty()) {
                DirectedGraphNode node = queue.poll();
                for (DirectedGraphNode n : node.neighbors) {
                    map.put(n,map.get(n) - 1);
                    if (map.get(n) == 0) {
                        results.add(n);
                        queue.offer(n);
                    }
                }
            }
            return results;
        }
    }
    View Code

    ***矩阵和图的对比

    图 Graph N个点,M条边 M最大是 O(N^2) 的级别

    图上BFS时间复杂度 = O(M) 所以最坏情况可能是 O(N^2)

    矩阵 Matrix N行M列 N*M个点,N*M*2 条边(每个点上下左右4条边,每条边被2个点共享)。

    矩阵中BFS时间复杂度 = O(N * M)

    6、矩阵中的BFS

  • 相关阅读:
    POJ 3259 Wormholes【BellmanFord】
    POJ 2960 SNim【SG函数的应用】
    ZOJ 3578 Matrixdp水题
    HDU 2897 邂逅明下【bash博弈】
    BellmanFord 算法及其优化【转】
    【转】几个Java的网络爬虫
    thinkphp 反字符 去标签 自动加点 去换行 截取字符串 冰糖
    php 二维数组转 json文本 (jquery datagrid 数据格式) 冰糖
    PHP 汉字转拼音(首拼音,所有拼音) 冰糖
    设为首页与加入收藏 兼容firefox 冰糖
  • 原文地址:https://www.cnblogs.com/lingli-meng/p/6557349.html
Copyright © 2011-2022 走看看