算法系列15.2——图相关基本算法

  • 宽度优先遍历,bfs,只需要Node即可,不需要edge

    • 一个队列,一个set,set为queue服务,已经进过队列的点不会再进队列
    • 入队之前检查是否在set里,入队的同时入set
  • 在很经常的情况下,点集合的哈希表结构可以简化成数组结构

    • 在常数级上进行优化
    • 原因是通常点的value值就是一个整形编号可以作为下标
    • 而且范围通常不会特别大
  • 深度优先遍历,dfs,用栈来实现

    • 一个栈,一个set
    • 与bfs不同,深度优先遍历是加到栈里的时候就要进行处理,bfs是出队的时候才进行处理
    • 比宽度优先遍历更加复杂
      • 从栈中弹出
      • 遍历他的nexts
      • 如果还有不在set中的next
        • 将当前点压回栈中
        • 将这个next压到栈顶,加入set并处理
        • break回到第一步
      • 直到栈为空
    • 即是
      • 处理完当前的点之后,或者是下方处理完返回到这一层之后
      • 会检查他的所有邻居
      • 有路没有走过就往下走走到无路可走,否则继续往上返回
  • 拓扑排序算法

    • 适用条件:有向无环图, 没有循环依赖
    • 首先看入度为0,一定是排在最前面的A
    • 将A及他的影响擦掉,找下一个入度为0的点
    • 编程实现
      • 一张哈希表inMap,key是Node,value是这个点当前剩余的入度
      • 一个队列,入度为0的点入队
      • 记录初始inmap,入度为0入对
        • 出队,加入链表结果数组
        • 所有的next,在inmap中入度-1
        • 新的入度为0的点加入队列
  • kruskal算法(ElogE)

    • 要求:无向图

    • 从边的角度出发,先把所有的边排序,每次选最小的边

    • 每一次只需要考察加入当前边之后会不会形成环

    • 需要并查集结构

      • 一开始所有的点的集合都只有自己,所有的点都不连通

      • 考察某一条边的时候,考察from和to所在的集合是否是同一个集合

        • 是,不可用,会形成环,看下一条更大的边
        • 不是,可以用,from和to的集合要合并
      • 并查集实现快,查询和合并都是常数级别,先用一个比较慢的实现(合并不是常数集)

        public static class MySets{
          public HashMap<Node, List<Node>> setMap;
          
          
          public MySets(List<Node> nodes) {
            for(Node cur : nodes) {
              List<Node> Set = new ArrayList<Node>();
              set.add(cur);
              setMap.put(cur, set);
            }
          }
          
          
          public boolean isSameSet(Node from, Node to) {
            List<Node> fromSet = setMap.get(from);
            List<Node> toSet = setMap.get(to);
            return fromSet == toSet;
          }
          
          
          public void union(Node from, Node to) {
            List<Node> fromSet = setMap.get(from);
            List<Node> toSet = setMap.get(to);
            for(Node toNode : toSet) {
              fromSet.add(toNode);
              setMap.put(toNode, fromSet);
            }
          }
        }
        
    • 定义一个比较器,自己做一个堆,优雅的每次弹出最小的边

  • prim算法,同样要求是无向图(VLogE)

    • 一开始所有的边都没有被解锁
    • 选择一个点之后,相关的边被解锁
    • 从所有被解锁没有被选过的边中调一个最小的边
    • 一定要能够拽进一个新的点(边中一个点不在哈希表set中),产生新的解锁边
    • k算法需要一个集合的查询和合并,因为有已经连成两片的东西连在一起
    • 而p算法只需要一个哈希表,因为点是一个个加到哈希表中的
    • 编程实现
      • 所有解锁的边放入小根堆
      • 一个hashset检查两个点是否都已经在里面
      • set.add
      • priorityQueue.add
      • if(!set.contains)
      • 这个代码会重复放入边,进优先队列,但是不影响结果,增加了一点常数时间
      • 要注意处理森林的问题,不连通的图,可能各自要生成最小生成树
  • dijkstra算法,单源最短路径算法(没有权值为负的边)

    • 一定要规定出发点,算出到所有其它点的最短距离

    • 有一种启发式算法A*,比dijkstra效率更好

    • 初始化如下的记录表

      在这里插入图片描述

    • 每次从记录表中挑一个最小的进行处理

    • 查看这个点向外发散的所有边,看能否使其他点的距离更新变小

    • 如果能则更新,使用所有边之后,当前点的记录就固定了

    • 继续挑最小的进行处理

    • 如果有权值为负的边,可能某一时刻,某一个锁定的点结论就不对了

    • 其实完整的条件是不能有累加和权值为负数的环,可以有权值为负的边

    • dijkstra也有一个改进方法,需要一个自己改进的堆

    • 最基础的是,每一次都遍历选出一个最小的点

    • 貌似可以用堆,但是某一个较大的值突然变小,堆里的东西就需要修改

    • 系统实现的堆不能临时改完然后修改让堆进行修改,只能自己手动实现堆

用pq优化后复杂度mlogn,原复杂度n^2

bellmanford算法,有负边查找最短路,查找负环,复杂度mn

floyd算法,查找所有点对最短路

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值