• 五、宽度优先搜索(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

  • 相关阅读:
    LeetCode | Construct Binary Tree from Inorder and Postorder Traversal
    LeetCode | Construct Binary Tree from Preorder and Inorder Traversal
    LeetCode | Binary Tree Zigzag Level Order Traversal
    PLI与Pillow
    CentOS下安装setuptools、pip和virtualenv
    CentOS下更新python版本
    CentOS中输入yum报错:sudo: unable to execute /bin/yum: No such file or directory
    Python中pip版本升级error:You are using pip version 7.1.2, however version 8.1.1 is available.
    CentOS安装git
    CentOS中yum安装软件时报错:No package XXX available
  • 原文地址:https://www.cnblogs.com/lingli-meng/p/6557349.html
Copyright © 2020-2023  润新知