宽度优先搜索&图论

1. BFS的适用场景

1)分层遍历

  • 一层一层地遍历一个图、树或矩阵
  • 简单图最短路径(简单图的定义是,图中的所有边长都一样)

2)连通块问题

  • 通过图中一个点找到其他所有连通的点
  • 找到所有方案问题的一种非递归实现方式

3)拓扑排序

  • 实现容易度远超DFS 

2. BFS实现方式 (分层遍历)

1) 单队列方式

public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> result = new ArrayList<>();
    if(root == null){
        return result;
    }

    Queue<TreeNode> queue = new LinkedList<>();
    //step1: 把第一层节点放入队列中
    queue.offer(root);
    //step2: 当队列非空时,遍历该层节点   
    while(!queue.isEmpty()){
        int size = queue.size();
        List<Integer> level = new ArrayList<>();
        //step3: 由上一层节点,拓展出下一层节点
        for(int i=0;i<size;i++){
            TreeNode node = queue.poll();
            level.add(node.val);
            if(node.left != null){
                queue.offer(node.left);
            }
            if(node.right != null){
                queue.offer(node.right);
            }
        }
        result.add(level);
    }
    return result;
}

2)双队列方式 

思路:将一层的节点放入一个队列中,遍历该队列的所有节点,将所有子节点放入另一个队列,以此类推直至队列为空

public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> result = new ArrayList<>();
    if(root == null){
        return result;
    }
    //因为不需要用到poll功能来删除队首元素,用数组保存节点就可以
    List<TreeNode> queue = new ArrayList<>();
    queue.add(root);

    while(!queue.isEmpty()){
        //用一个新的数组来存放下一层的节点
        List<TreeNode> next_queue = new ArrayList<>();
        result.add(toIntegerList(queue));

        for(TreeNode node:queue){
            if(node.left != null){
                next_queue.add(node.left);
            }
            if(node.right != null){
                next_queue.add(node.right);
            }
        }
        queue = next_queue;
    }
    return result;
}

private List<Integer> toIntegerList(List<TreeNode> queue){
    List<Integer> level = new ArrayList<>();
    for(TreeNode node:queue){
        level.add(node.val);
    }
    return level;
}

3) DummyNode方式实现

思路:在每层节点间放一个虚拟节点(null),用来识别分层

public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> result = new ArrayList<>();
    if(root == null){
        return result;
    }

    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);
    queue.offer(null);
    List<Integer> level = new ArrayList<>();
    while(!queue.isEmpty()){
        TreeNode node = queue.poll();
        if(node == null){
            if(level.size() == 0){
                break;
            }
            result.add(level);
            queue.offer(null);
            level = new ArrayList<Integer>();
            continue;
        }

        level.add(node.val);
        if(node.left != null){
            queue.offer(node.left);
        }
        if(node.right != null){
            queue.offer(node.right);
        }
    }
    return result;
}

3. 图论 

1)什么是图 (Graph) ?

图是顶点(vertex) 和边(edge) 的集合,分为有向图和无向图。另外,树也是一种特殊的图。

2)BFS树 vs BFS图

在二叉树中做BFS与在图中做BFS最大的区别就是,二叉树中无需使用HashSet来存储已访问过的节点。因为二叉树这种数据结构上下层关系分明,没有环。

3)存储图的数据结构

  • 邻接矩阵 Adjacency Matrix:用一个二维数组 int[][] matrix,来表示每两个点之间是否连通
  • 邻接表 Adjacency List:每个点上存储自己有哪些邻居。

可以使用HashMap和HashSet搭配的方式来存储邻接表

Map<Integer,Set> adList = new HashMap<Integer,HashSet<>>(); 

也可以使用自定义的类来实现邻接表

class DirectedGraphNode{
    int label;
    List<DirectedGraphNode> neighbors;
    ......
}

4. 总结

1)广度优先搜索

涉及到层序遍历、连通块、最短路径等问题,大多都用BFS。

首先需要一个队列queue存储每一层的节点,不断拓展出下一层。

其次还需要创建一个数组visited (若在涉及网格、坐标等问题中,需要创建二维数组) 或 HashSet集合visited,用来标明访问过的节点。

2)双向广度优先搜索

当明确知道目标顶点的情况下,可以分别从起点和终点执行广度优先遍历,直到遍历的部分有交集。

可以用两个集合 HashSet来分别存储从起点BFS的该层节点,和从终点BFS的该层节点。并且需要一个总的集合visited来存放已访问过的节点。

5. 相关例题

Leetcode: 102. 二叉树的层序遍历1293. 网格中的最短路径127. 单词接龙,

LintCode: 1179 · 朋友圈, 630 · 骑士的最短路径II

6. 拓展

1)三种遍历

  • 先序遍历 - 根左右
  • 中序遍历 - 左根右
  • 后序遍历 - 左右根

2)break & continue 

break用法:break用于switch语句中,终止switch语句;break用于循环时,跳出循环。

continue用法:continue用于循环中,跳过本次循环,继续执行下一次循环。

3)HashSet

  1. 简介:HashSet是基于HashMap实现的,是一个不允许有重复元素的集合。HashSet允许有null元素,是无序的。
  2. 无参构造:例 HashSet<String> set = new HashSet<String>();
  3. 参数为 (Collection) 另一个集合来构造:HashSet<String> set = new HashSet<>(wordList); wordList是一个数组
  4. add() :add方法调用了HashMap的put方法。例 set.add("peter");
  5. contains() :用来判断元素是否存在于集合当中,底层调用了HashMap的containsKey方法,返回值类型boolean。例 set.contains("peter")
  6. remove() :用来删除集合中的指定元素,底层调用了HashMap的remove方法,返回值类型boolean。例 set.remove("peter");
  7. clear方法:用来删除集合中所有元素。例 set.clear();  

问:树图关系的解析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值